Tuesday, August 30, 2016

Exploring React’s State Propagation

This article is part of a web development series from Microsoft. Thank you for supporting the partners who make SitePoint possible.

An earlier article explored data within a React component. Data is represented by two structures–properties and state. The former is used to represent immutable data, while the latter represents data that is changed through interactions with the UI or through other external means.

When working with state data, the UI is updated when the state is changed via a call to the setState function. Typically, this function is invoked in response to an event using an event handler.

In this post, we will further explore the updating of state, including form input components and the propagation of state values through the properties of child components. Finally, we will look at the Immutable JavaScript library created by Facebook in order to understand more efficient ways to notify React when a component needs to be re-rendered.

Input Components

Let’s consider the following React Component, which contains a form with one input element, and a change event registered on the input element.


var Message = React.createClass({ 
   
  getInitialState: function() { 
    return { message: this.props.message }; 
  }, 
   
  _messageChange: function(e) { 
    this.setState({ message: e.target.value }); 
  }, 
 
  render: function() { 
    return ( 
      <div> 
        <span>Message: {this.state.message}</span> 
        <br /> 
        Message: <input type="text" value={this.state.message} onChange={this._messageChange} /> 
      </div> 
    ); 
  }, 
 
}); 

When the user enters text into the text input box, a change event handler is executed, capturing the value from the textbox, and then updating the state. For this example, the change event handler is _messageChange. If no onChange event handler is registered, then the field will be read, but the state will not be updated with the input data. In React, input controls do not update themselves, they update the state, and then the state triggers a re-render to update the input control. On the surface this approach seems a little convoluted, but it is the key to the way React always keeps the state of a component in sync with the DOM.

The standard HTML form input controls such as input, select and textarea elements are treated as input components by React. Because these controls can change value, React provides a controlled mechanism by which the controls can be initialized, receive input, and be updated to reflect that input in the UI.

Explore React state propagation 1

Input components can be either controlled or uncontrolled. Controlled components are managed by React through the value and onChange properties. When the user enters text into the input element, the registered onChange event handler is executed and the text entered is passed as an argument via an events object. The text argument is used to update the state which is then passed back into the controlled component via props. A form component with the value property set but no onChange property, will be read only as mentioned earlier.

So why is the input component read only? As described earlier, with controlled input components, the input control itself is not directly updated by interacting with it. Instead of being directly updated, the change event is fired. To capture the new value, this event must be handled, and the passed event object is used to access the new value. Then, the new value is used to update the state of the input component’s parent component. In the example above, this parent component is Message. Calling the parent’s setState function re-renders the input component, and the updated state value is passed back into the input component via its props. Why this approach? The view (in this case, the DOM) and the state of the React component must always be the same, which is not possible using traditional uncontrolled input elements.

Consider the following non-React code.


<main> 
  <input name="message" value="Hello World!"> 
  <button>Get Value!</button> 
</main> 

When the user enters text into the input control, the input control will display the text that was entered. After the user enters text into the input control, and the button is clicked, what do you think the output is?


document.getElementsByTagName("button")[0].addEventListener("click", function() { 
   
  console.log(document.querySelector("[name='message']").getAttribute("value")); 
   
}); 

Interestingly, the output is NOT the updated text typed into the box, but rather, the original value of the attribute when the input control was rendered. While the updated text is displayed, the DOM is now out of sync with the state of the input control.

To see this action, try out the following CodePen.

See the Pen React.js Uncontrolled Input Demo by SitePoint (@SitePoint) on CodePen.

For many JavaScript libraries and frameworks, this is not an issue. However, for React, its Virtual DOM and component state should always be synchronized.

Consider the following CodePen demonstration.

See the Pen React.js Controlled / Uncontrolled Input Demo by SitePoint (@SitePoint) on CodePen.

Enter text into the first input box, and observe how only the first input box updates. Enter text into the second input box, and observe how both input boxes update. Because the second input box does not bind to the value property, when the message updates, that update is not reflected in the second input box. Because the second input box handles the change event through the onChange property, the state is updated, where the changed message value is updated in the first input box, then displayed on the screen. The defaultValue property of the second input box is only used when the input component is rendered the first time.

An input component is uncontrolled if it does not have its value property set, and updates in the UI normally when interacting with the component, but no re-rendering will occur as a result of state changes.

To explore additional form input component functionality, consider the Color List demonstration described in the next two sections.

Propagation of State through Child Components

Developers who are new to React often wonder whether data is stored in the props or the state. As mentioned in earlier posts, the props are an immutable structure and they are the preferred way to pass data into components. State is a mutable structure which triggers the component to re-render when it changes. The answer to the earlier question–whether data is stored in the props or the state – is both. Choosing props or state has less to do with what the data is, and more to do with the relationship of the data to the overall component structure. Data passed in as props could then be used to initialize state. State data is then passed in as props to a child component. The determination of whether to use props or state primarily centers on the relationship of the data to the component, as well as the relationship of the component to other components.

Composing components is a common pattern in React. In the code example below, there are three components: Color, ColorList, and ColorForm. Color is the parent or container component for ColorList and ColorForm. As the parent component, Color is responsible for maintaining the state and triggering the re-rendering of its child components.


Parent Color Component 
getInitialState: function() { 
  return { 
    colors: new Immutable.List(this.props.colors) 
  }; 
}, 
 
render: function() { 
  return ( 
    
<ColorList colors={this.state.colors} /> <ColorForm addColor={this._addColor} />
  
  ); 
} 

To propagate the values of the state from the parent component to the child, the state values are passed into the child component via props as shown in the parent’s render function.

The child accesses the passed-in props through the props property on the component, as shown below.


Child Color List Component 
render: function() { 
  return ( 
    <ul>
      {this.props.colors.map(function(color) { 
        return <li key={color}>{color}</li>; 
      })} 
    </ul> 
  ); 
} 

Observe the data flow–the parent component receives data via its props. Those props are used to initialize the state of the parent, then the parent passes the state data to its children via their props. The children then use the props to render themselves.

So the same data is viewed both as immutable props and mutable state, depending upon the purpose of the component receiving the data. The reason the parent component handles the data as mutable state is that it can handle events from a child component which passes up new data, triggering the state to change, then passes the updated state to all of the child components. The child component is not responsible for updating anything with the new data, it simply passes that data along to its parent component which performs the update. This results in an easy to understand and predictable data flow.

Continue reading %Exploring React’s State Propagation%


by Eric Greene via SitePoint

No comments:

Post a Comment