nicholas nicholas - 2 months ago 16
React JSX Question

ReactJS state vs prop

This may be treading that line between answerable and opinionated, but I'm going back and forth as to how to structure a ReactJS component as complexity grows and could use some direction.

Coming from AngularJS, I want to pass my model into the component as a property and have the component modify the model directly. Or should I be splitting the model up into various

state
properties and compiling it back together when sending back upstream? What is the ReactJS way?

Take the example of a blog post editor. Trying to modify the model directly ends up looking like:

var PostEditor = React.createClass({
updateText: function(e) {
var text = e.target.value;
this.props.post.text = text;
this.forceUpdate();
},
render: function() {
return (
<input value={this.props.post.text} onChange={this.updateText}/>
<button onClick={this.props.post.save}/>Save</button>
);
}
});


Which seems wrong.

Is it more the React way to make our
text
model property
state
, and compile it back into the model before saving like:

var PostEditor = React.createClass({
getInitialState: function() {
return {
text: ""
};
},
componentWillMount: function() {
this.setState({
text: this.props.post.text
});
},
updateText: function(e) {
this.setState({
text: e.target.value
});
},
savePost: function() {
this.props.post.text = this.state.text;
this.props.post.save();
},
render: function() {
return (
<input value={this.state.text} onChange={this.updateText}/>
<button onClick={this.savePost}/>Save</button>
);
}
});


This doesn't require a call to
this.forceUpdate()
, but as the model grows, (a post may have an author, subject, tags, comments, ratings, etc...) the component starts getting really complicated.

Is the first method with ReactLink the way to go?

jxg jxg
Answer

Your second approach is more like it. React doesn't care about models so much as it cares about values and how they flow through your app. Ideally, your post model would be stored in a single component at the root. You then create child components that each consume parts of the model.

You can pass callbacks down to the children that need to modify your data, and call them from the child component.

Modifying this.props or this.state directly is not a good idea, because React will not be able to pick up on the changes. That's because React does a shallow comparison of your post prop to determine if it has changed.

I made this jsfiddle to show how data could flow from an outer to an inner component:

http://jsfiddle.net/jxg/M3CLB/

The handleClick method shows 3 ways to (im)properly update state:

var Outer = React.createClass({

  getInitialState: function() {
    return {data: {value: 'at first, it works'}};
  },

  handleClick: function () {

    // 1. This doesn't work, render is not triggered.
    // Never set state directly because the updated values
    // can still be read, which can lead to unexpected behavior.

    this.state.data.value = 'but React will never know!';

    // 2. This works, because we use setState

    var newData = {value: 'it works 2'};
    this.setState({data: newData});

    // 3. Alternatively you can use React's immutability helpers
    // to update more complex models.
    // Read more: http://facebook.github.io/react/docs/update.html

    var newState = React.addons.update(this.state, {
      data: {value: {$set: 'it works'}}
    });
    this.setState(newState);
 },

  render: function() {
      return <Inner data={this.state.data} handleClick={this.handleClick} />;
  }
});