41

I am passing a react element as a prop to another element. In the child element that receives the prop, I need to set additional props to that element.

For example:

Parent class

class Menu Extends React.Component {
    render() {
        return(
            <div className="Menu">
                <MenuItem icon={<MdInbox />} />
                <MenuItem icon={<MdDrafts />} />
                <MenuItem icon={<MdTrash />} />
            </div>
        );
    }
}

Child class

class MenuItem Extends React.Component {
    render() {
        return(
            <div className="MenuItem">
                {this.props.icon} // I want to set the icon's size prop here
            </div>
        );
    }
}

this.props.icon is a React element (<MdInbox />, <MdTrash />, etc), and it allows for a property size. I want to set the size property in the MenuItem class, as opposed to passing the prop in from the parent like this: <MenuItem icon={<MdInbox size={24} />}. I'd prefer just to set the size in one place only, within the MenuItem class.

Seth McClaine
  • 9,142
  • 6
  • 38
  • 64
sme
  • 4,023
  • 3
  • 28
  • 42
  • React Element is immutable, therefore you cannot pass additional props or change it afterwards (reactjs.org/docs/glossary.html#elements) – Eric Kim Nov 19 '18 at 11:29

3 Answers3

42

Pass in the component constructor instead of an instance:

class Menu extends React.Component {
    render() {
        return(
            <div className="Menu">
                <MenuItem icon={MdInbox} />
                <MenuItem icon={MdDrafts} />
                <MenuItem icon={MdTrash} />
            </div>
        );
    }
}

The child class:

class MenuItem extends React.Component {
    render() {
        // This constant must begin with a capital,
        // it’s how React distinguishes HTML elements from components.
        const Icon = this.props.icon;
        return(
            <div className="MenuItem">
                <Icon size={24} />
            </div>
        );
    }
}
Denis
  • 5,061
  • 1
  • 20
  • 22
  • 1
    Ha, I actually like this resolution better :-) The only concern would be if the op wanted to add other data specific to the type (not called out in the example though) – Seth McClaine Apr 14 '18 at 14:31
  • 4
    Thanks! As for adding other props, it is possible to use **React.cloneElement**, i.e.: `React.cloneElement(Icon, { onClick: this.open, size: 24 })`. – mcmimik Feb 09 '19 at 21:15
  • It actually doesn't work for me. My App crashes for some reason. Is there a related documentation for this usage? – Alex May 11 '20 at 03:28
  • @Alex, that will be too specific to your application, I think you need a new question. – Denis Jun 04 '20 at 12:59
  • @Denis i think we need a function while passing icon prop in MenuItem, else react throws error saying expected function/class got object. – Harkirat Saluja Aug 06 '20 at 07:21
  • Could you update your solution for functional components as well, if your know how to do that? Would be great – Tobi Sep 02 '20 at 11:19
  • @Tobi, class vs functional components has zero impact on this question and this solution. I suggest to look for guidance on functional components elsewhere. – Denis Sep 03 '20 at 12:05
  • @Denis , using your example, do you know of a way to pass props to MdInbox in Menu component, **and** in MenuItem component ? – Ambroise Rabier Nov 03 '20 at 15:26
26

You can set icon size with React.cloneElement API

class MenuItem Extends React.Component {
    render() {
        return(
            <div className="MenuItem">
                {React.cloneElement(this.props.icon, { size: 24 })}
            </div>
        );
    }
}
Vadim Shvetsov
  • 2,126
  • 2
  • 20
  • 28
  • This should be the selected answer as it sticks to the format of the prop passed in (node, instead of elementType). – sayandcode Aug 03 '22 at 08:05
0

You can also just pass the component and props to the child elemnt, fully destructure your component and props, then render it as a JSX element. You then can spread your props into the passed component. This way your child component reusable and you can change its props from the parent component.

class Menu extends React.Component {
        render() {
            return(
                <div className="Menu">
                    <MenuItem Icon={MdInbox} size={24}/>
                    <MenuItem Icon={MdDrafts} size={24} />
                    <MenuItem Icon={MdTrash} size={24}/>
                </div>
            );
        }
    }

The child class:

    class MenuItem extends React.Component {
        render() {
            // This constant must begin with a capital,
            // Use JS ...rest operator to seperate Icon from remaining props
            const {Icon, ...remainingProps} = this.props;
            return(
                <div className="MenuItem">
                    <Icon {...remainingProps}/>
                </div>
            );
        }
    }
}