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:
- Original element's props are shallow merged with new props;
- New children replace existing children;
key
andref
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 ref
s 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.