How to Add New Props to an Existing React Component?

One of the most common use cases for adding new props to an existing React component is, for example, adding new props to children that are passed down to an existing component. In this article, we will show you how you can achieve that.

Using React.cloneElement()

One of the ways we can add new props to the children is by using React.cloneElement() like so:

const newProps = { className: 'hidden', onClick: () => alert('test') };

const MyComponent = ({ children }) => (
    <div>
        {children.map(child => React.cloneElement(child, newProps))}
    </div>
);

Using React.cloneElement() does the following:

  1. Original element's props are shallow merged with new props;
  2. New children replace existing children;
  3. key and ref from the original element are preserved.

The following example illustrates how React keeps the same ref and key from the original elements when using React.cloneElement():

const newProps = {
    className: 'hidden',
    onClick: (e) => alert(e.target.textContent),
};
const MyComponent = ({ children }) => (
    <div>
        {children.map(child => React.cloneElement(child, newProps))}
    </div>
);
const App = () => {
    const fooRef = React.useRef(null);
    const barRef = React.useRef(null);

    React.useEffect(() => {
        console.log(fooRef.current.className); // 'hidden'
        console.log(barRef.current.className); // 'hidden'
    }, []);

    return (
        <MyComponent>
            <div key="foo" ref={fooRef}>foo</div>
            <div key="bar" ref={barRef}>bar</div>
        </MyComponent>
    );
};

ReactDOM.render(<App />, document.getElementById('app'));

As you can see from the code above, the rendered children are going to have a hidden class and an onClick event handler. Another important thing to note here is that App still has reference to the children we passed down to MyComponent.

Merging New & Old Props

One other way we can add new props to an existing element is by doing something like the following:

const newProps = {
    className: 'hidden',
    onClick: (e) => alert(e.target.textContent),
};
const MyComponent = ({ children }) => (
    <div>
        {children.map((child, index) => (
            <child.type key={child.key || index} ref={child.ref} {...child.props} {...newProps}>
                {child.props.children}
            </child.type>
        ))}
    </div>
);

In the code above you can see that we're preserving the key and refs here as well. To demonstrate this, consider the following:

const App = () => {
    const fooRef = React.useRef(null);
    const barRef = React.useRef(null);

    React.useEffect(() => {
        console.log(fooRef.current.className); // 'hidden'
        console.log(barRef.current.className); // 'hidden'
    }, []);

    return (
        <MyComponent>
            <div key="foo" ref={fooRef}>foo</div>
            <div key="bar" ref={barRef}>bar</div>
        </MyComponent>
    );
};

ReactDOM.render(<App />, document.getElementById('app'));

As expected, our rendered elements have a hidden class and an onClick event handler, as well as the original element's ref and key.


This post was published by Daniyal Hamid. Daniyal currently works as the Head of Engineering in Germany and has 20+ years of experience in software engineering, design and marketing. Please show your love and support by sharing this post.