Material-ui: [MenuItem][IconMenu] Menu doesn't close on click in wrapped MenuItems

Created on 10 Feb 2017  路  6Comments  路  Source: mui-org/material-ui

Problem description

I am using an IconMenu within the RightAppMenu of an Appbar - and this works perfectly with MenuItem children. I am trying to do some routing (react-router-redux) when one of the MenuItems is clicked, so created a wrapper component around MenuItem:

import React from 'react';
import MenuItem from 'material-ui/MenuItem';
import { connect } from 'react-redux';
import { push } from 'react-router-redux';

const MenuItemLink = ({to, primaryText, push, ...props}) => {
    return (
        <MenuItem
            onTouchTap={() => push(to)}
            primaryText={primaryText}
            {...props}
        >
        </MenuItem>
    )
};

MenuItemLink.propTypes = {
    to: React.PropTypes.string,
    primaryText: React.PropTypes.string,
    push: React.PropTypes.func
};

const mapDispatchToProps = {
    push
};

export default connect(null, mapDispatchToProps)(MenuItemLink);

This works fine in terms of rendering and triggering the route change action, but it does not close the IconMenu as happens when the non-wrapped MenuItems are clicked. Is there any way to get this behaviour?

Many thanks

Versions

  • Material-UI: 0.16.7
  • React: 15.4.2
  • Browser: Chrome Version 56.0.2924.76 (64-bit)

Most helpful comment

Ah, so the key is wrappedComponent.muiName = 'MenuItem';! Thanks for the hint @oliviertassinari!

My implementation of MenuItemLink (not sure if perfect):

import React from 'react';
import MenuItem from 'material-ui/MenuItem';
import { Route } from 'react-router-dom';

class MenuItemWithLink extends React.PureComponent {
  render() {
    const {
      to,
      ...rest
    } = this.props;

    return (
      <Route
        render={({ history, location }) => (
          <MenuItem
            disabled={location.pathname === to}
            onClick={() => {
              history.push(to);
            }}
            {...rest}
          />
        )}
      />
    );
  }
}
MenuItemLink.muiName = 'MenuItem';
export default MenuItemLink;

All 6 comments

We have started documenting how to wrap components on the next branch.
It's the same approach for the master branch.
Basically, you need to add:

const wrappedComponent = connect(null, mapDispatchToProps)(MenuItemLink);
wrappedComponent.muiName = 'MenuItem';

export default wrappedComponent;

Thanks for your quick reply. I tried the approach you gave and it resolves the menu closing issue, but it appears that the onTouchTap prop is no longer being fired. I have reproduced this in a WebpackBin at http://www.webpackbin.com/EJoeuIDdf - the wrapper component there is:

````javascript
import React from 'react';
import { MenuItem } from 'material-ui';

const MenuItemLink = props => {
return (
onTouchTap={() => { console.log("fired"); bin.log("fired"); }}
{...props}
>

)
};

// With the line below the menu auto closes but "fired" is not logged.
// Wth it commented out "fired" is logged but the menu
// remains open.
MenuItemLink.muiName = 'MenuItem';

export default MenuItemLink;
````

Thanks for your time.

but it appears that the onTouchTap prop is no longer being fired

That's expected as the Menu component inject his own onTouchTap. You need to call it back.
From your use case, it seems that what you really want is not wrapping the MenuItem but making so he use the Link component of react-router.

I hope it helps, I won't have more time to help.

Ah, so the key is wrappedComponent.muiName = 'MenuItem';! Thanks for the hint @oliviertassinari!

My implementation of MenuItemLink (not sure if perfect):

import React from 'react';
import MenuItem from 'material-ui/MenuItem';
import { Route } from 'react-router-dom';

class MenuItemWithLink extends React.PureComponent {
  render() {
    const {
      to,
      ...rest
    } = this.props;

    return (
      <Route
        render={({ history, location }) => (
          <MenuItem
            disabled={location.pathname === to}
            onClick={() => {
              history.push(to);
            }}
            {...rest}
          />
        )}
      />
    );
  }
}
MenuItemLink.muiName = 'MenuItem';
export default MenuItemLink;

@alsiola I would avoid using PureComponent but that looks good to me.

Using material-ui-next:

MenuItemLink.js

import React from 'react';
import { MenuItem } from 'material-ui/Menu';
import { Route } from 'react-router-dom';

class MenuItemLink extends React.Component {
  render() {
    const {
      to, also,
      ...rest
    } = this.props;

    return (
      <Route
        render={({ history, location }) => (
          <MenuItem
            onClick={() => {
              history.push(to);
              if (typeof also === 'function') {
                also();
              }
            }}
            {...rest}
          />
        )}
      />
    );
  }
}
MenuItemLink.muiName = 'MenuItem';
export default MenuItemLink;

In my header:

import MenuItemLink from './MenuItemLink';

...

    this.handleRequestClose = () => {
      this.setState({ anchorEl: null });
    }

...

              <MenuItemLink to="/admins"
                also={this.handleRequestClose}>Admins</MenuItemLink>
...


Was this page helpful?
0 / 5 - 0 ratings