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.
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