1
votes

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

2
where you put the console inside children, can you show the code?Mayank Shukla
I've added it but I don't know if it will be much of a helpElon Salfati
have you tried using jsx? its easier to specify props using jsx syntaxMaru
I'm always getting a different component there so I'm not sure how jsx will do the job hereElon Salfati

2 Answers

3
votes

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)

2
votes

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>