Elon Salfati
Elon Salfati

Reputation: 1687

React pass props to children

I'm trying to pass extra props to this.props.children, I saw this answer how to pass props to children with React.cloneElement?

and from some reason although I'm not getting any error I can't see the prop

So I have this state

this.state = {
    open: true
}

and I want to pass it down to this.props.children, and this is what I've done so far:

{
    React.Children.map(this.props.children, child =>
        React.cloneElement(child, {sidebarState: this.state.open}))
}

and when I'm console.logging this.props on the children I can't see my new props.

--- EDIT --- In the children it looks like this:

render() {
    console.log(this.props)
    // other code
}

BTW I'm using react 16.0

Upvotes: 5

Views: 12964

Answers (2)

Vadorequest
Vadorequest

Reputation: 17999

Here is an example.

Before (without passing props down to the children):

    <div className="layout">
      {children}
    </div>

After (passing extra props1 and props2 props to every child):

    <div className="layout">
      {
        React.Children.map(children, (child) => {
          return React.cloneElement(child, {
            props1: 1,
            props2: 2,
          });
        })
      }
    </div>

props1 and props2 get merged with the existing props for every child.

Regarding TypeScript types, you have to use React.ReactElement instead of React.ReactNode or the TS compiler will complain at React.Children.map (or ts-ignore it):

type Props = {
  children: React.ReactElement;
};

See https://medium.com/better-programming/passing-data-to-props-children-in-react-5399baea0356 for more explanation, that helped me a lot to understand how to do it! (#mediumPaywall)

Upvotes: 7

Ori Drori
Ori Drori

Reputation: 191936

There are two ways to pass props to children:

Children as function

Instead of being a React element, children can be a function.

Call the children function:

const List = ({ children, sidebarState }) => (
  <ul>
  {
    children(sidebarState)
  }
  </ul>
);

Passing the children a function:

<List sidebarState={sidebarState}>
  {
    (sidebarState) => (
      <Item sidebarState={sidebarState} />
    )
  }
</List>

Working example:

const { Component } = React;

const Item = ({ sidebarState }) => (
  <li>{sidebarState ? 'open' : 'close'}</li>
);

const List = ({ children, sidebarState }) => (
  <ul>
  {
    children(sidebarState)
  }
  </ul>
);

class App extends Component {
  constructor(props) {
    super(props);
    
    this.state = {
        sidebarState: true
    }
  }
  
  toggleOpen = () => this.setState((prevState) => ({
    sidebarState: !prevState.sidebarState
  }));
  
  render() {
    const { sidebarState } = this.state;
    
    return (
      <div>
        <button onClick={this.toggleOpen}>Toggle</button>
        
        <List sidebarState={sidebarState}>
          {
            (sidebarState) => (
              <Item sidebarState={sidebarState} />
            )
          }
        </List>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  demo
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>

React.cloneElement

Working example:

const { Component } = React;

const Item1 = ({ sidebarState }) => (
  <li>{sidebarState ? 'open' : 'close'}</li>
);

const Item2 = ({ sidebarState }) => (
  <li>{sidebarState ? 'open' : 'close'}</li>
);

const List = ({ children, sidebarState }) => (
  <ul>
  {
    React.Children.map(children, (child) => React.cloneElement(child, { sidebarState }))
  }
  </ul>
);

class App extends Component {
  constructor(props) {
    super(props);
    
    this.state = {
        sidebarState: true
    }
  }
  
  toggleOpen = () => this.setState((prevState) => ({
    sidebarState: !prevState.sidebarState
  }));
  
  render() {
    const { sidebarState } = this.state;
    
    return (
      <div>
        <button onClick={this.toggleOpen}>Toggle</button>
        
        <List sidebarState={sidebarState}>
          <Item1 />

          <Item2 />
        </List>
      </div>
    );
  }
}

ReactDOM.render(
  <App />,
  demo
);
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<div id="demo"></div>

Upvotes: 3

Related Questions