1

I have Avatar and Menu components inside the Navbar component. I want to trigger the function from Menu component by clicking on Avatar component. My question is how to pass a clickEvent from avatar to Menu component.

<AppBar position="sticky" className={classes.navBarStyle}>
        <Toolbar>
            <Avatar alt="" className={classes.userAvatar} src="./avatar.jpg"/>
            <DropDownMenu/>
        </Toolbar>
    </AppBar>

function DropDownMenu() {
    const [anchorEl, setAnchorEl] = React.useState(null);
    const open = Boolean(anchorEl);

    const handleClick = (event) => {
        setAnchorEl(event.currentTarget);
      };

    const handleClose = () => {
      setAnchorEl(null);
    };
  
    return (
      <div>
        <Menu
          id="fade-menu"
          anchorEl={anchorEl}
          keepMounted
          open={open}
          onClose={handleClose}
          TransitionComponent={Fade}
        >
          <MenuItem onClick={handleClose}>Profile</MenuItem>
          <MenuItem onClick={handleClose}>My account</MenuItem>
          <MenuItem onClick={handleClose}>Logout</MenuItem>
        </Menu>
      </div>
    );
}

1
  • I'd suggest changing the title to "how to control a Material-UI Menu from another component", I think it would be more helpful for other users that way Commented Jan 20, 2021 at 18:33

3 Answers 3

3

You don't need to pass the click event around, you just need to move the anchorEl state to your component with AppBar and pass anchorEl and onClose to DropDownMenu as props:

function MainAppBar() {
    const [anchorEl, setAnchorEl] = React.useState(null);

    const handleClick = (event) => {
        setAnchorEl(event.currentTarget);
      };

    const handleClose = () => {
      setAnchorEl(null);
    };

    <AppBar position="sticky" className={classes.navBarStyle}>
        <Toolbar>
            <Avatar alt="" className={classes.userAvatar} src="./avatar.jpg" onClick={handleClick} />
            <DropDownMenu anchorEl={anchorEl} onClose={handleClose} />
        </Toolbar>
    </AppBar>
}

function DropDownMenu({ anchorEl, onClose }) {
    const open = Boolean(anchorEl);

    return (
      <div>
        <Menu
          id="fade-menu"
          anchorEl={anchorEl}
          keepMounted
          open={open}
          onClose={onClose}
          TransitionComponent={Fade}
        >
          <MenuItem onClick={handleClose}>Profile</MenuItem>
          <MenuItem onClick={handleClose}>My account</MenuItem>
          <MenuItem onClick={handleClose}>Logout</MenuItem>
        </Menu>
      </div>
    );
}

I wrote a library, material-ui-popup-state, that is less cumbersome to use for cases like this:

import {bindTrigger, bindMenu, usePopupState} from 'material-ui-popup-state/hooks';

function MainAppBar() {
    const popupState = usePopupState({ variant: 'popover', popupId: 'fade-menu' });

    <AppBar position="sticky" className={classes.navBarStyle}>
        <Toolbar>
            <Avatar {...bindTrigger(popupState)} alt="" className={classes.userAvatar} src="./avatar.jpg" />
            <DropDownMenu popupState={popupState} />
        </Toolbar>
    </AppBar>
}

function DropDownMenu({ popupState }) {
    return (
      <div>
        <Menu
          {...bindMenu(popupState)}
          keepMounted
          TransitionComponent={Fade}
        >
          <MenuItem onClick={popupState.close}>Profile</MenuItem>
          <MenuItem onClick={popupState.close}>My account</MenuItem>
          <MenuItem onClick={popupState.close}>Logout</MenuItem>
        </Menu>
      </div>
    );
}
Sign up to request clarification or add additional context in comments.

6 Comments

Thanks, I thought about this solution, but I wonder if there is any way to pass clickEvent to another component, eg. via ref
You can't really pass a click event to another component. But all you need from the click event in this case is a DOM element to anchor the menu to (event.currentTarget). And you certainly can use a ref to get a different DOM element to use for anchorEl.
At least, I don't think it's to make another DOM element emit the click event. Even if there is, it would be considered hacky, most programmers wouldn't want you to solve the problem that way. It's possible to pass the event to a different function, and you could engineer a way to pass the event to a function within DropDownMenu, but it would be more complicated than this, and less convenient to use in the long run.
OK. I was looking for a long time if it even possible to relate two components in this way, but there is no information now I know why. Once more thanks
Using Material UI menus and popups can be a bit awkward though, especially in cases like this. I actually wrote this library to try to make it easier: github.com/jcoreio/material-ui-popup-state
|
0

The trick is to place the function in the parent component that and are both children of, and pass it down as a prop, that way it can be a shared function.

https://reactjs.org/docs/faq-functions.html

Comments

0

You have ToolBar component which is parent of your avatar and menudropdown components. Place those handleClick, handleClose and const [anchorEl, setAnchorEl] = React.useState(null); in parent component of those childs.

Next pass handleClose funtion to dropDownMenu component and handleClick to Avatar component as props. You should also pass state to those components who base their actions on it, in this case to menuDropDowncomponent because i see that he need this state data.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.