5

So I am trying to pass some props from my top level component to a child component, I have done some searching online but cannot find anything that shows how I can pass this.props.children WITH some values my component's state. Here is my code.

Layout (Parent):

export default class Layout extends React.Component {
    constructor (props) {
    super(props)
    this.state = { data: 'test' }
  }

    render() {
        const {location} = this.props;
        console.log("layout");
        return (
            <div>
                <Nav location={location}/>
                <div className="container">
                    <div className="row">
                        <div className="col-lg-12">

                            {this.props.children}, data={this.state.data}

                        </div>
                    </div>
                    <Footer/>
                </div>
            </div>

        );
    }
}

When I call the "data" props in my next Component:

Home (Child):

//ON COMPONENT RENDER
    componentDidMount = () => {
        console.log("home");
        console.log(this.props.data);
    }

In my console it returns:

home

Undefined

Any pointers to how I should be doping this? Any help is appreciated, thank you in advance.

Community
  • 1
  • 1
daniel aagentah
  • 1,672
  • 5
  • 29
  • 56
  • Are you sure this syntax is correct? (` {this.props.children}, data={this.state.data} `) – Dekel Feb 14 '17 at 16:18
  • I'm not entirely sure mate, I got that code from a LearnCodeAcademy react tutorial, though it's not the way I normally pass props. – daniel aagentah Feb 14 '17 at 16:25
  • To rule out an XY problem: *why* are you doing this? There is no guarantee that `children` is a single element, it could just as easily be an array: then what happens? And if this is a build system in which you can *guarantee* that `this.props.children` is actually just a single element, then please explain that with some code that shows how you're actually using this. For skeleton bootstrapping `cloneElement` is the correct solution (you already have an answer for it), but "relying on children" is also an antipattern outside of that so: what are you doing that makes you think this is needed. – Mike 'Pomax' Kamermans Feb 14 '17 at 16:48
  • @Mike'Pomax'Kamermans this.props.children is a single element (component), all I wanted to do was pass some state with that component as it is being rendered from "Layout" – daniel aagentah Feb 14 '17 at 17:13
  • then cloneElement will do fine. – Mike 'Pomax' Kamermans Feb 14 '17 at 17:33
  • Possible duplicate of [How to pass props to {this.props.children}](https://stackoverflow.com/questions/32370994/how-to-pass-props-to-this-props-children) – stone Dec 01 '17 at 01:03

2 Answers2

7

If you're trying to add a prop to the children directly, this won't really work since components are ment to be immutable. What you should do instead is create a map with clones of the children.

This blog post explains it fairly well: http://jaketrent.com/post/send-props-to-children-react/

And the relevent code snippets altered for your code:

class Layout extends React.Component {
  constructor (props) {
    super(props)
    this.state = { data: 'test' }
  }

  renderChildren() {
    return React.Children.map(this.props.children, child => {
      if (child.type === Child) {
        return React.cloneElement(child, {
          data: this.props.data
        })
      } else {
        return child
      }
    });
  }

  render() {
    const {location} = this.props;
    console.log("layout");
    return (
        <div>
            <Nav location={location}/>
            <div className="container">
                <div className="row">
                    <div className="col-lg-12">
                        {this.renderChildren()}
                    </div>
                </div>
                <Footer/>
            </div>
        </div>
    );
  }
}
tdf
  • 457
  • 4
  • 14
  • That sounds like an issue with the component that your router is rendering. Specifcally it needs to return a single element and not something like `
    `
    – tdf Feb 14 '17 at 17:06
1

Do a console.log on this.state.data in your Layout component. I'm thinking its undefined there too.

I think you need to set your state before the constructor super() call or statically outside the constructor.

Edit: So this.props.children is a react element? You need to clone it to pass it different props.

React.cloneElement(this.props.children, {
    name: props.name
})
Zoxive
  • 116
  • 2
  • 4
  • +1 Agreed about testing this.state.data in Layout component. Just a note, the order he has in the constructor is fine for setting state in es6 – Chris Feb 14 '17 at 16:22
  • I just ran console.log(this.state.data); in the Layout's render, its returns "test" fine, I think my problem lies in how I am passing it to the child component, good pointer though. – daniel aagentah Feb 14 '17 at 16:24
  • Oh this.props.children is a react component? You probably need to clone it. React.cloneElement(child, { data: this.state.data }) – Zoxive Feb 14 '17 at 16:26
  • I believe @Zoxive is correct, see my answer. Could you update your question with how you tried implementing it using React.cloneElement? – tdf Feb 14 '17 at 16:38