3
votes

Here is a small piece of code that I've tried out:

var CommentBox = React.createClass({
        loadCommentsFromServer: function () {
            $.ajax({
                url: this.props.url,
                dataType: 'json',
                type: 'GET',
                success: function (data) {
                    this.setState({data: data});
                }.bind(this),
                error: function (xhr, status, err) {
                    console.error(this.props.url, status, err.toString());
                }.bind(this),
                cache: false
            });
        },
        getInitialState: function () {
            return {data: []};
        },
        componentDidMount: function () {
            this.loadCommentsFromServer();
        },
        render: function () {
            return (
                    <div className="commentBox">
                        <MessageBox1 data={this.state.data}/>
                    </div>
            );
        }
    });

    var MessageBox1 = React.createClass({
        getInitialState: function() {
            alert('MessageBox1 getInitialState');
            return {nameWithQualifier: 'Mr. ' + this.props.data.pageName};
        },
        componentDidMount: function() {
            alert('this.props.data: ' + this.props.data);
        },
        render: function() {
            return (<div>
                        <div>{this.state.nameWithQualifier}</div>
                        <div> {this.props.data.pageName}</div>
                    </div>
            );
        }
    });

ReactDOM.render(<CommentBox url="/save/6"/>, document.getElementById('content'));

In CommentBox, I'm querying an object with ID 6 and passing it to MessageBox1 component. I want to make this object a state variable in MessageBox1 component. The problem here is, I'm not able to read the props variable. "this.props.data" is undefined in the getInitialState and ComponentDidMount of MessageBox1 component. Whereas, in the render function I can see the correct value. I've tried few examples where I'm able to read the props data in getInitialState. Why isn't this happening here? Please help. I don't have any errors in the console.

1

1 Answers

2
votes

I believe you are having some confusion with the react component life cycle.

I recommend quickly reading through this article to understand when each of the life cycle methods will be invoked. http://busypeoples.github.io/post/react-component-lifecycle/

In messagebox1 in your getInitialState method, initialize your state structure like this to prevent accessing the props before they're available.

{ nameWithQualifier: '' }

You are calling an async action to fetch the data so there is no guarantee when the data will be retrieved. However based on how you have it now (on componentDidMount) it will always be retrieved after messageBox1 is initialized which explains why this.props.data is undefined in getInitialState. I recommend triggering your loadCommentsFromServer in componentWillMount to trigger the async action as early as possible, and it is possible to setState before the component mounts.

Instead of setting the state of messageBox1 in setInitialState try using componentWillReceiveProps like this. It will guarantee that your state updates when the props change.

componentWillReceiveProps(newProps) {
    this.setState({ nameWithQualifier: 'Mr. ' + newProps.data.pageName })
}

However IF by chance the async data is available before this nested messageBox1 component renders you may also want to do this on messageBox1 to ensure the state is up to date on the initial render:

componentWillMount() {
    this.setState({ nameWithQualifier: 'Mr. ' + this.props.data.pageName })
}

So if you really want to do this, I recommend the 2 suggestions above

  1. Call your ajax request in componentWillMount to get the request sent off earlier
  2. update your state in your child component on componentWillReceiveProps (which will update your state every time the props update)

However setting your components state with it's props doesn't seem necessary in most cases. Maybe try something like this:

in the commentBox Component's render function send the pageName down as a prop:

   render: function () {
        return (
                <div className="commentBox">
                    <MessageBox1 pageName={this.state.data.pageName}/>
                </div>
        );
    }

And in the messageBox render function you can simplify the process by accessing the props directly. It will give you the same result.

    render: function() {
        return (<div>
                    <div>{'Mr. ' + this.props.pageName}</div>
                    <div> {this.props.pageName}</div>
                </div>
        );
    }

This will work for this simple case unless you for some reason need to change the props within messageBox1 because props are immutable by the component they reside in. They will only update if the parent component changes what is passed down.