Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass props to {this.props.children}

I'm trying to find the proper way to define some components which could be used in a generic way:

<Parent>   <Child value="1">   <Child value="2"> </Parent> 

There is a logic going on for rendering between parent and children components of course, you can imagine <select> and <option> as an example of this logic.

This is a dummy implementation for the purpose of the question:

var Parent = React.createClass({   doSomething: function(value) {   },   render: function() {     return (<div>{this.props.children}</div>);   } });  var Child = React.createClass({   onClick: function() {     this.props.doSomething(this.props.value); // doSomething is undefined   },   render: function() {     return (<div onClick={this.onClick}></div>);   } }); 

The question is whenever you use {this.props.children} to define a wrapper component, how do you pass down some property to all its children?

like image 854
plus- Avatar asked Sep 03 '15 08:09

plus-


People also ask

Can I pass props to props children?

Passing Props to Children in React Using the Context APIContext allows you to pass props down to an entire tree of components. It is extremely useful because you have an App component at the top but want to pass down an event handler to a child component at the bottom of the tree.

How do you pass a prop to child component?

There is no way to pass props up to a parent component from a child component. We will revisit this caveat later in this tutorial. It's also important to note that React's props are read only (immutable). As a developer, you should never mutate props but only read them in your components.

How do you pass children in React?

Children are props If you want to pass things to this button, you would use a prop. If you want to make our button say more than just "I am a button," you can pass children to it. By passing children in this way, you are passing it to the component by position.


2 Answers

Cloning children with new props

You can use React.Children to iterate over the children, and then clone each element with new props (shallow merged) using React.cloneElement. For example:

const Child = ({ doSomething, value }) => (   <button onClick={() => doSomething(value)}>Click Me</button> );  function Parent({ children }) {   function doSomething(value) {     console.log("doSomething called by child with value:", value);   }    const childrenWithProps = React.Children.map(children, child => {     // Checking isValidElement is the safe way and avoids a typescript     // error too.     if (React.isValidElement(child)) {       return React.cloneElement(child, { doSomething });     }     return child;   });    return <div>{childrenWithProps}</div> }  function App() {   return (     <Parent>       <Child value={1} />       <Child value={2} />     </Parent>   ); }  ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> <div id="container"></div>

Calling children as a function

Alternatively, you can pass props to children with render props. In this approach, the children (which can be children or any other prop name) is a function which can accept any arguments you want to pass and returns the children:

const Child = ({ doSomething, value }) => (   <button onClick={() => doSomething(value)}>Click Me</button> );  function Parent({ children }) {   function doSomething(value) {     console.log("doSomething called by child with value:", value);   }    // Note that children is called as a function and we can pass args to it.   return <div>{children(doSomething)}</div> }  function App() {   // doSomething is the arg we passed in Parent, which   // we now pass through to Child.   return (     <Parent>       {doSomething => (         <React.Fragment>           <Child doSomething={doSomething} value={1} />           <Child doSomething={doSomething} value={2} />         </React.Fragment>       )}     </Parent>   ); }  ReactDOM.render(<App />, document.getElementById("container"));
<script src="https://unpkg.com/react@17/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@17/umd/react-dom.production.min.js"></script> <div id="container"></div>

Instead of <React.Fragment> or simply <> you can also return an array if you prefer.

like image 173
Dominic Avatar answered Sep 23 '22 02:09

Dominic


For a slightly cleaner way to do it, try:

<div>     {React.cloneElement(this.props.children, { loggedIn: this.state.loggedIn })} </div> 

Edit: To use with multiple individual children (the child must itself be a component) you can do. Tested in 16.8.6

<div>     {React.cloneElement(this.props.children[0], { loggedIn: true, testPropB: true })}     {React.cloneElement(this.props.children[1], { loggedIn: true, testPropA: false })} </div> 
like image 43
Andres F Garcia Avatar answered Sep 26 '22 02:09

Andres F Garcia