Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Understanding state in React

Understanding state in React

For the SF React meetup on May 19, 2014.

spicyj

June 19, 2014
Tweet

More Decks by spicyj

Other Decks in Technology

Transcript

  1. Multiple-choice widget JSON { "multipleSelect": true, "randomize": false, "choices": [

    { "content": "$x$ is a rational number.", "correct": true }, { "content": "$x$ has an infinite, non-repeating decimal...", "correct": false }, { "content": "$x$ has a terminating decimal representation.", "correct": true }, ... ] }
  2. Multiple-choice widget editor (Editor) var Editor = React.createClass({ render: function()

    { var choices = this.props.choices.map((choice, i) => <Choice ref={"choice" + i} content={choice.content} correct={choice.correct} onDelete={this.handleDelete.bind(null, i)} />); return <div>{choices}</div>; }, handleDelete: function(i) { var choices = this.props.choices.slice(); choices.splice(i, 1); rerender(); }, serialize: function() { return { choices: this.props.choices.map((choice, i) => this.refs["choice" + i].serialize()) }; } });
  3. Multiple choice widget editor (Choice) var Choice = React.createClass({ render:

    function() { return ( <div> <input type="text" ref="content" defaultValue={this.props.content} /> </div> ); }, serialize: function() { return { content: this.refs.content.getDOMNode().value }; } });
  4. Problems in this implementation — React did the “wrong” DOM

    operations — When the DOM changes, component state doesn’t — If data changes, the DOM doesn’t update to match
  5. React did the “wrong” DOM operations var oldChildren = [

    <Choice content="monkey" />, <Choice content="gorilla" />, <Choice content="giraffe" /> ]; var newChildren = [ <Choice content="gorilla" />, <Choice content="giraffe" /> ];
  6. React did the “wrong” DOM operations React guesses (unintelligently) about

    what key is: var oldChildren = [ <Choice key={0} content="monkey" />, <Choice key={1} content="gorilla" />, <Choice key={2} content="giraffe" /> ]; var newChildren = [ <Choice key={0} content="gorilla" />, <Choice key={1} content="giraffe" /> ]; …and deletes the last choice element.
  7. React did the “wrong” DOM operations { "choices": [ {

    "content": "monkey", "key": 42 }, { "content": "gorilla", "key": 13 }, ... ] } var choices = this.props.choices.map((choice, i) => <Choice key={choice.key} ... />);
  8. React did the “wrong” DOM operations Explicitly-specified keys: var oldChildren

    = [ <Choice key={42} content="monkey" />, <Choice key={13} content="gorilla" />, <Choice key={17} content="giraffe" /> ]; var newChildren = [ <Choice key={13} content="gorilla" />, <Choice key={17} content="giraffe" /> ]; Now, React deletes the first element, as expected.
  9. We need a single source of truth In this implementation,

    the DOM is usually the source of truth… except when it’s not. We don't know where to look to find what the value should be.
  10. Reasons to keep state out of the DOM — Ties

    the view implementation to the data — Harder to show derived data — Slower
  11. A gift from React: controlled components Previously: <input type="text" ref="content"

    defaultValue={this.props.content} /> After converting to a controlled component: <input type="text" ref="content" value={this.props.content} // always in sync! onChange={this.handleChange} />
  12. A gift from React: controlled components Controlled components take a

    bit more work upfront, but they have several advantages: — Model and view never get out of sync — Easy to tell what's in the DOM based on your data — Easier to have data where you want it already (i.e., no need for serialize or ref) (Bonus: the demo works now even without keys.)
  13. Best practices — Use key properly: if you don't have

    an appropriate key, add one to your data — Avoid keeping state in the DOM — Use controlled components — Read from the DOM only in event handlers — Think about where your source of truth is What do you get? Airtight UIs.