alpertuna / react-metismenu

A ready / simple to use, highly customizable, updateable, ajax supported, animated and material designed menu component for React

Home Page:https://www.npmjs.com/package/react-metismenu

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to pass props to custom link component

SlowburnAZ opened this issue · comments

commented

I seem to be having problems passing my own props to a custom link component. How should one do this?

For example, I have a "CustomMenuLink" that I'm using, pretty much following the example given in the README:

var CustomMenuLink = React.createClass({  
    onClick: function(e) {  
        if(this.props.hasSubmenu) this.props.toggleSubmenu(e);
        else {  
            this.props.linkClick();
        }
    },
    render: function() {
        return (
            <a href="" className="metismenu-link" onClick={this.onClick} >
                {this.props.children}
            </a>
        );
    }
});

If I do as suggested, and pass this component into the LinkComponet prop, what is the proper way to pass the "linkClick" prop to it? I've tried the following, and it certainly doesn't work:

var cml = React.cloneElement(CustomMenuLink, {linkClick: this.myLinkClickFunction});
return (
    <MetisMenu content={someContent} LinkComponent={cml} />
);

For now, It's not possible to pass any prop over react-metismenu to custom link component.
The way for this is importing needed modules to custom link then calling functions of that modules instead of linkClick.

It seems it is needed an on click event emitter for custom links to use it proper way. I will add this soon.

commented

Oh... that's a shame.

In the app I'm doing for contract work, the menu items will be dynamically added, so the "linkClick" function will need to have the ability to accept an argument that depends on the menu item itself. For example, clicking a "Dashboard" menu item will need to call a function like "linkClick('Dashboard')" so that it knows which link is being clicked. Without being able to pass anything into the custom link component, I'm not sure how that could be accomplished, even with importing an external module.

Do you have any other suggestions?

P.S. I've also updated the original post, as some of my example code was missing.

commented

In looking at your source code (src/components/Item.jsx), it seems that a custom click function could be passed in (customClickHandler):

/**
 * src/components/Container.jsx
 * Author: H.Alper Tuna <halpertuna@gmail.com>
 * Date: 16.09.2016
 */

import React, { PropTypes } from 'react';
import classnames from 'classnames';
import Container from '../containers/Container';

const Item = ({
  id,
  icon,
  label,
  to,
  externalLink,
  hasSubMenu,
  active,
  hasActiveChild,
  subMenuVisibility,
  toggleSubMenu,
  activateMe,
  customClickHandler,
}, {
  classStore,
  LinkComponent,
}) => (
  <li
    className={classnames(
      classStore.classItem,
      active && classStore.classItemActive,
      hasActiveChild && classStore.classItemHasActiveChild,
      (hasSubMenu && subMenuVisibility) && classStore.classItemHasVisibleChild
    )}
  >
    <LinkComponent
      className={classStore.classLink}
      classNameActive={classStore.classLinkActive}
      classNameHasActiveChild={classStore.classLinkHasActiveChild}
      active={active}
      hasActiveChild={hasActiveChild}
      to={to}
      externalLink={externalLink}
      hasSubMenu={hasSubMenu}
      toggleSubMenu={toggleSubMenu}
      activateMe={activateMe}
      customClickHandler={customClickHandler}
    >
      <i className={classnames(classStore.classIcon, classStore.iconNamePrefix + icon)} />
      {label}
      {hasSubMenu && <i
        className={classnames(
          classStore.classStateIcon,
          classStore.iconNamePrefix + (
            subMenuVisibility
              ? classStore.iconNameStateVisible
              : classStore.iconNameStateHidden
          )
        )}
      />}
    </LinkComponent>
    {hasSubMenu && <Container
      itemId={id}
      visible={subMenuVisibility}
    />}
  </li>
);

Item.propTypes = {
  id: PropTypes.oneOfType([
    PropTypes.number,
    PropTypes.string,
  ]).isRequired,
  icon: PropTypes.string,
  label: PropTypes.oneOfType([
    PropTypes.element,
    PropTypes.array,
    PropTypes.string,
  ]),
  to: PropTypes.string,
  externalLink: PropTypes.bool,
  hasSubMenu: PropTypes.bool.isRequired,
  active: PropTypes.bool.isRequired,
  hasActiveChild: PropTypes.bool.isRequired,
  subMenuVisibility: PropTypes.bool.isRequired,
  toggleSubMenu: PropTypes.func,
  activateMe: PropTypes.func.isRequired,
  customClickHandler: PropTypes.func
};

Item.contextTypes = {
  classStore: PropTypes.object.isRequired,
  LinkComponent: PropTypes.oneOfType([
    PropTypes.element,
    PropTypes.func,
  ]).isRequired,
};

export default Item;

Seems like then you could configure the menu content like so:

const content=[
    {
        icon: 'icon-class-name',
        label: 'Label of Item',
        to: '#a-link'
        customClickHandler: this.linkClick('firstItem')
    },
    {
        icon: 'icon-class-name',
        label: 'Second Item',
        content: [
            {
                icon: 'icon-class-name',
                label: 'Sub Menu of Second Item',
                to: '#another-link',
                customClickHandler: this.linkClick('secondItem')
            },
        ],
    },
];

return (
    <MetisMenu content={content} LinkComponent={CustomMenuLink} />
);

Would something like that work, or be desirable?

commented

Ah yes... that wasn't clicking in my mind before. I can see now how that would work with importing my function in the custom link component.

I'll give that a shot. Thanks.

You're welcome 😊
Soon event emitting support will be added. Then you will be able to dispatch an event not to import any other module.

Usage will be like this;

// In your app
<MetisMenu content={...} LinkComponent={...} onSelected={this.onMenuSelected} />
// In custom link component
onClick() {
//...
this.props.emit('selected', /* To, label or any other object to send listener as parameter */);
//...
}
commented

Now that I'm thinking about it, I may still run into a problem if I need to bind something to the function I want to run, like if my function needs to do something like:

linkClick('Dashboard').bind(this, somethingElse);

Hmm... in the custom link component, I may still not be able to bind the 'this', which would be the main component I'm using MetisMenu in.

How long do you think it might be before you get the event emitter code in place?

In two days I will publish something enough which does the job for you.

commented

You rock, @alpertuna !

Hi @SlowburnAZ,
I publish minor version to npm repos; v1.2

Relevent README parts;

Example
In your app

<MetisMenu content={...} onSelected={this.onMenuSelected} />

In your custom link component

onClick() {
  // ...
  this.props.activateMe(/* "this.props.to", "this.props.label" or any other object to send "onSelected" listener as parameter */);
  // ...
}

props.activateMe is already used to highlight selected active link. Now it is also used to triger onSelected listeners and send parameters to them.

I will be waiting for your feedback.

commented

Hey, @alpertuna

I've installed this latest version. I've got things working as expected now, but I did notice that inside the onClick function in the LinkComponent, that this.props doesn't have the 'label' property available. It does have the 'to' property, though, so that works for me, but I figured I'd mention it in case someone else needs it.

Also, if I set the 'id' property of the menu items (what's passed to "content" on the MetisMenu component), this also is not available in the LinkComponent in this.props.

You were right, label and id props were not passed to custom links. Now its fixed via v1.2.2.
Thanks for your note.