The book React Fast. Web applications in React, JSX, Redux and GraphQL »
Hi, habrozhiteli! The original edition was released in the fall of 2017, but is still considered the best book for exploring React. The author is constantly updating and modifying the code for the book in the Github repository .
We offer in a post to get acquainted with the passage "States and their role in the interactive nature of React"
If you had to read only one chapter in this book - it would be worth choosing this one! Without states, React components remain nothing more than advanced static patterns. I hope you share my enthusiasm because understanding the concepts in this chapter will allow you to build much more interesting applications.
Imagine that you are building an input field with autocomplete (Fig. 4.1). When entering data, the field should issue a request to the server to get information about suitable options for displaying output on a web page. You have worked with properties so far, and you know that changing properties allows you to get different views. However, properties cannot be changed in the context of the current component, because they are passed when the component is created.
In other words, the properties are immutable in the current component, which means that you cannot change the properties in this component unless you re-create the component and transfer new values from the parent (Fig. 4.2). But the information received from the server needs to be stored somewhere, and then a new list of options should be displayed in the view. How to update the view if the properties cannot be changed?
One possible solution is to render an element with new properties every time you receive a new response from the server. But then you have to place the logic outside the component - and the component ceases to be self-sufficient. Obviously, if property values cannot be changed, and autocompletion should be self-sufficient, it is impossible to use properties. Then the question arises: how to update views in response to events without re-creating the component (createElement () or JSX)? It is this problem that states solve.
After the response from the server is ready, the callback code will change the state of the component accordingly. You will have to write this code yourself. However, after the state is updated, React will automatically update the view for you (only in the places where it should be updated, that is, where the state data is used).
With the state of React components, you can build interactive, meaningful React applications. State is a fundamental concept that allows you to build React components that can store data and automatically update views in accordance with changes in the data.
The React state is a mutable component data storage - stand-alone function-oriented blocks of the user interface and logic. “Variability” means that state values can change. Using state in the view (render ()) and changing the values later, you can influence the appearance of the view.
Metaphor: if you imagine a component as a function, to the input of which properties and state are transferred, the result of the function will be a description of the user interface (presentation). Properties and states expand views, but they are used for different purposes (see section 4.3).
When working with states, you access them by name. The name is an attribute (that is, an object key or an object property - not a component property) of this.state object, for example this.state.autocompleMatches or this.state.inputFieldValue.
State data is often used to display dynamic information in a view to extend the rendering of views. Returning to an earlier example of an autocomplete field: the state changes in response to an XHR request to the server, which, in turn, is initiated by entering data into the field. React ensures that views are updated when the state used in views changes. In fact, when a state changes, only the corresponding parts of the representations change (to individual elements and even the attribute values of an individual element).
Everything else in the DOM remains unchanged. This is possible thanks to the virtual DOM model (see section 1.1.1), which React uses to determine the delta (totality of changes) during the reconciliation process. This fact allows you to write code in a declarative style. React does all the routine for you. The main stages of changing the presentation are discussed in Chapter 5.
React developers use states to generate new user interfaces. Component properties (this.props), ordinary variables (inputValue), and class attributes (this.inputValue) are not suitable for this, because changing their values (in the context of the current component) does not trigger a change in the view. For example, the following snippet is antipattern, which shows that changing the value anywhere except the state will not cause the view to refresh:
Now let's see how to work with the states of React components.
To work with states, you must be able to access values, update them, and set initial values. Let's start by referring to states in React components.
The state object is an attribute of the component, and you should access it through the this link, for example this.state.name. As you recall, variables can be accessed and displayed in JSX code in curly braces ({}). Similarly, in render (), you can render this.state (like any other variable or class attribute of a non-standard component), for example {this.state.inputFieldValue}. This syntax is similar to the syntax for accessing properties in this.props.name.
We use what you learned to implement the clock in fig. 4.3. Our goal is to create an autonomous component class that anyone can import and use in their application without much hassle. The clock should display the current time.
The project has the following structure:
I use the Babel CLI with tracking flags (-w) and directory (-d) to compile all JSX source files from clock / jsx to the target clock / js folder and recompile when changes are detected. In addition, I saved the command as an npm script in the package.json file of the parent folder ch04 to execute the npm run build-clock command from ch04:
Of course, time does not stand still (whether we like it or not). Because of this, you need to constantly update the view, and for this you can use the state. Name it currentTime and try to render the state as shown in Listing 4.1.
Listing 4.1. JSX state render
You will receive an error message: Uncaught TypeError: Cannot read property 'currentTime' of null. Usually JavaScript error messages have about the same benefits as a glass of cold water for a drowning person. It’s good that at least in this case, JavaScript displays a meaningful message.
The message indicates that the value of currentTime is undefined. Unlike properties, states are not set in the parent. Calling setState in render () also fails, because it will create a loop (setState -> render -> setState ...) - and React will report an error.
You have already seen that before using state data in render (), you must initialize the state. To set the initial state, use this.state in the constructor with the syntax of the ES6 class React.Component. Remember to call super () with properties; otherwise, the logic in the parent (React.Component) will not work:
When assigning the initial state, you can also add other logic - for example, set the value of currentTime using new Date (). You can even use toLocaleString () to get the correct date / time format for the user's current location, as shown below (ch04 / clock).
Listing 4.2. Clock component constructor
The value of this.state must be an object. We will not go into details of constructor () from ES6; refer to Appendix D and the ES6 Summary at github.com/azat-co/cheatsheets/tree/master/es6 . The bottom line is that, like in other OOP languages, a constructor (i.e. constructor ()) is called when an instance of the class is created. The name of the constructor method should be just that; consider this one of the rules of ES6. In addition, when creating the constructor () method, the super () call should almost always be included in it, without which the constructor of the parent would not be executed. On the other hand, if you do not define the constructor () method, then the call to super () will be assumed by default.
The name currentTime is optional; you must use the same name later when reading and updating this state.
The state object may contain nested objects or arrays. The following example adds an array of book descriptions to the state:
The constructor () method is called only once, when creating a React element based on the class. Thus, you can set the state directly using this.state only once - in the constructor () method. Do not set or update the state directly with this.state = ... somewhere else, as this can lead to unforeseen consequences.
So you get only the initial value, which will very quickly become obsolete - in just 1 second. Who needs a watch that does not show the current time? Fortunately, there is a mechanism for updating the current state.
The state is changed by the method of the this.setState (data, callback) class. When this method is called, React combines the data with the current states and calls render (), after which it calls callback.
Defining a callback callback in setState () is important because the method works asynchronously. If the application depends on the new state, you can use this callback to make sure that the new state has become available.
If you simply assume that the state has been updated without waiting for setState () to complete, that is, to work synchronously when performing an asynchronous operation, an error may occur: the program depends on updating the state values, but the state remains old.
So far, we have rendered time from state. You already know how to set the initial state, but it should be updated every second, right? To do this, use the browser timer function setInterval () (http://mng.bz/P2d6), which will update the state every n milliseconds. The setInterval () method is implemented in almost all modern browsers as global, which means that it can be used without any additional libraries or prefixes. Example:
To start the countdown, you need to call setInterval () only once. We create the launchClock () method solely for this purpose; launchClock () will be called in the constructor. The final version of the component is shown in Listing 4.3 (ch04 / clock / jsx / clock.jsx).
The setState () method can be called anywhere, not just in the launchClock () method (which is called in the constructor), as in the example. Typically, the setState () method is called from an event handler or as a callback when data is received or updated.
It is important to note that the setState () method only updates the states that were passed to it (partially or merged, but without a complete replacement). It does not replace the entire state object every time. Therefore, if only one of the three states has changed, the other two will remain unchanged. In the following example, userEmail and userId will not change:
If you intend to update all three states, you will have to do this explicitly by passing the new values of these states to setState (). (Also in the old code, which now no longer works, the this.replaceState () method is sometimes found; it is officially deprecated.1. As you might guess by name, it replaced the entire state object with all its attributes.)
Remember that calling setState () initiates the execution of render (). In most cases, it works. In some special cases in which the code depends on external data, you can initiate a re-render by calling this.forceUpdate (). Nevertheless, such decisions are undesirable, because relying on external data (instead of state) makes the components less reliable and dependent on external factors (tight binding).
As mentioned earlier, the state object can be accessed in the this.state entry. In JSX, the output values are enclosed in curly braces ({}), therefore, to declare a state property in a view (that is, in the return command of the render method), use the notation {this.state.NAME}.
React magic occurs when you use state data in a view (for example, in output, in an if / else command, as an attribute value or property value of a child), and then pass setState () new values. Bah! React updates all the necessary HTML markup for you. You can verify this in the DevTools console, where the cycles "Updating ..." and "Rendering ..." should be displayed. And the great thing is that this will only affect the absolute minimum required DOM elements.
»More information about the book can be found on the publisher’s website
» Contents
» Excerpt
For Khabrozhiteley 20% discount on coupon - React
Upon payment of the paper version of the book, an electronic version of the book is sent by e-mail.
We offer in a post to get acquainted with the passage "States and their role in the interactive nature of React"
If you had to read only one chapter in this book - it would be worth choosing this one! Without states, React components remain nothing more than advanced static patterns. I hope you share my enthusiasm because understanding the concepts in this chapter will allow you to build much more interesting applications.
Imagine that you are building an input field with autocomplete (Fig. 4.1). When entering data, the field should issue a request to the server to get information about suitable options for displaying output on a web page. You have worked with properties so far, and you know that changing properties allows you to get different views. However, properties cannot be changed in the context of the current component, because they are passed when the component is created.
In other words, the properties are immutable in the current component, which means that you cannot change the properties in this component unless you re-create the component and transfer new values from the parent (Fig. 4.2). But the information received from the server needs to be stored somewhere, and then a new list of options should be displayed in the view. How to update the view if the properties cannot be changed?
One possible solution is to render an element with new properties every time you receive a new response from the server. But then you have to place the logic outside the component - and the component ceases to be self-sufficient. Obviously, if property values cannot be changed, and autocompletion should be self-sufficient, it is impossible to use properties. Then the question arises: how to update views in response to events without re-creating the component (createElement () or JSX
After the response from the server is ready, the callback code will change the state of the component accordingly. You will have to write this code yourself. However, after the state is updated, React will automatically update the view for you (only in the places where it should be updated, that is, where the state data is used).
With the state of React components, you can build interactive, meaningful React applications. State is a fundamental concept that allows you to build React components that can store data and automatically update views in accordance with changes in the data.
What is the state of the React component?
The React state is a mutable component data storage - stand-alone function-oriented blocks of the user interface and logic. “Variability” means that state values can change. Using state in the view (render ()) and changing the values later, you can influence the appearance of the view.
Metaphor: if you imagine a component as a function, to the input of which properties and state are transferred, the result of the function will be a description of the user interface (presentation). Properties and states expand views, but they are used for different purposes (see section 4.3).
When working with states, you access them by name. The name is an attribute (that is, an object key or an object property - not a component property) of this.state object, for example this.state.autocompleMatches or this.state.inputFieldValue.
State data is often used to display dynamic information in a view to extend the rendering of views. Returning to an earlier example of an autocomplete field: the state changes in response to an XHR request to the server, which, in turn, is initiated by entering data into the field. React ensures that views are updated when the state used in views changes. In fact, when a state changes, only the corresponding parts of the representations change (to individual elements and even the attribute values of an individual element).
Everything else in the DOM remains unchanged. This is possible thanks to the virtual DOM model (see section 1.1.1), which React uses to determine the delta (totality of changes) during the reconciliation process. This fact allows you to write code in a declarative style. React does all the routine for you. The main stages of changing the presentation are discussed in Chapter 5.
React developers use states to generate new user interfaces. Component properties (this.props), ordinary variables (inputValue), and class attributes (this.inputValue) are not suitable for this, because changing their values (in the context of the current component) does not trigger a change in the view. For example, the following snippet is antipattern, which shows that changing the value anywhere except the state will not cause the view to refresh:
// Антипаттерн: не делайте так!
let inputValue = 'Texas'
class Autocomplete extends React.Component {
updateValues() ← { Инициируется в результате действия пользователя (ввод данных)
this.props.inputValue = 'California'
inputValue = 'California'
this.inputValue = 'California'
}
render() {
return (
{this.props.inputValue}
{inputValue}
{this.inputValue}
)
}
}
Now let's see how to work with the states of React components.
Work with states
To work with states, you must be able to access values, update them, and set initial values. Let's start by referring to states in React components.
Access to states
The state object is an attribute of the component, and you should access it through the this link, for example this.state.name. As you recall, variables can be accessed and displayed in JSX code in curly braces ({}). Similarly, in render (), you can render this.state (like any other variable or class attribute of a non-standard component), for example {this.state.inputFieldValue}. This syntax is similar to the syntax for accessing properties in this.props.name.
We use what you learned to implement the clock in fig. 4.3. Our goal is to create an autonomous component class that anyone can import and use in their application without much hassle. The clock should display the current time.
The project has the following structure:
/clock
index.html
/jsx
script.jsx
clock.jsx
/js
script.js
clock.js
react.js
react-dom.js
I use the Babel CLI with tracking flags (-w) and directory (-d) to compile all JSX source files from clock / jsx to the target clock / js folder and recompile when changes are detected. In addition, I saved the command as an npm script in the package.json file of the parent folder ch04 to execute the npm run build-clock command from ch04:
"scripts": {
"build-clock": "./node_modules/.bin/babel clock/jsx -d clock/js -w"
},
Of course, time does not stand still (whether we like it or not). Because of this, you need to constantly update the view, and for this you can use the state. Name it currentTime and try to render the state as shown in Listing 4.1.
Listing 4.1. JSX state render
class Clock extends React.Component {
render() {
return {this.state.currentTime}
}
}
ReactDOM.render(
,
document.getElementById('content')
)
You will receive an error message: Uncaught TypeError: Cannot read property 'currentTime' of null. Usually JavaScript error messages have about the same benefits as a glass of cold water for a drowning person. It’s good that at least in this case, JavaScript displays a meaningful message.
The message indicates that the value of currentTime is undefined. Unlike properties, states are not set in the parent. Calling setState in render () also fails, because it will create a loop (setState -> render -> setState ...) - and React will report an error.
Assignment of the initial state
You have already seen that before using state data in render (), you must initialize the state. To set the initial state, use this.state in the constructor with the syntax of the ES6 class React.Component. Remember to call super () with properties; otherwise, the logic in the parent (React.Component) will not work:
class MyFancyComponent extends React.Component {
constructor(props) {
super(props)
this.state = {...}
}
render() {
...
}
}
When assigning the initial state, you can also add other logic - for example, set the value of currentTime using new Date (). You can even use toLocaleString () to get the correct date / time format for the user's current location, as shown below (ch04 / clock).
Listing 4.2. Clock component constructor
class Clock extends React.Component {
constructor(props) {
super(props)
this.state = {currentTime: (new Date()).toLocaleString()}
}
...
}
The value of this.state must be an object. We will not go into details of constructor () from ES6; refer to Appendix D and the ES6 Summary at github.com/azat-co/cheatsheets/tree/master/es6 . The bottom line is that, like in other OOP languages, a constructor (i.e. constructor ()) is called when an instance of the class is created. The name of the constructor method should be just that; consider this one of the rules of ES6. In addition, when creating the constructor () method, the super () call should almost always be included in it, without which the constructor of the parent would not be executed. On the other hand, if you do not define the constructor () method, then the call to super () will be assumed by default.
The name currentTime is optional; you must use the same name later when reading and updating this state.
The state object may contain nested objects or arrays. The following example adds an array of book descriptions to the state:
class Content extends React.Component {
constructor(props) {
super(props)
this.state = {
githubName: 'azat-co',
books: [
'pro express.js',
'practical node.js',
'rapid prototyping with js'
]
}
}
render() {
...
}
}
The constructor () method is called only once, when creating a React element based on the class. Thus, you can set the state directly using this.state only once - in the constructor () method. Do not set or update the state directly with this.state = ... somewhere else, as this can lead to unforeseen consequences.
So you get only the initial value, which will very quickly become obsolete - in just 1 second. Who needs a watch that does not show the current time? Fortunately, there is a mechanism for updating the current state.
Status update
The state is changed by the method of the this.setState (data, callback) class. When this method is called, React combines the data with the current states and calls render (), after which it calls callback.
Defining a callback callback in setState () is important because the method works asynchronously. If the application depends on the new state, you can use this callback to make sure that the new state has become available.
If you simply assume that the state has been updated without waiting for setState () to complete, that is, to work synchronously when performing an asynchronous operation, an error may occur: the program depends on updating the state values, but the state remains old.
So far, we have rendered time from state. You already know how to set the initial state, but it should be updated every second, right? To do this, use the browser timer function setInterval () (http://mng.bz/P2d6), which will update the state every n milliseconds. The setInterval () method is implemented in almost all modern browsers as global, which means that it can be used without any additional libraries or prefixes. Example:
setInterval(()=>{
console.log('Updating time...')
this.setState({
currentTime: (new Date()).toLocaleString()
})
}, 1000)
To start the countdown, you need to call setInterval () only once. We create the launchClock () method solely for this purpose; launchClock () will be called in the constructor. The final version of the component is shown in Listing 4.3 (ch04 / clock / jsx / clock.jsx).
The setState () method can be called anywhere, not just in the launchClock () method (which is called in the constructor), as in the example. Typically, the setState () method is called from an event handler or as a callback when data is received or updated.
TIP An attempt to change the state in the code with a command of the form this.state.name = 'new name' will not lead to anything. It will not lead to re-rendering and updating the real DOM model, what you would like. In most cases, a direct state change without setState () is antipattern and should be avoided.
It is important to note that the setState () method only updates the states that were passed to it (partially or merged, but without a complete replacement). It does not replace the entire state object every time. Therefore, if only one of the three states has changed, the other two will remain unchanged. In the following example, userEmail and userId will not change:
constructor(props) {
super(props)
this.state = {
userName: 'Azat Mardan',
userEmail: 'hi@azat.co',
userId: 3967
}
}
updateValues() {
this.setState({userName: 'Azat'})
}
If you intend to update all three states, you will have to do this explicitly by passing the new values of these states to setState (). (Also in the old code, which now no longer works, the this.replaceState () method is sometimes found; it is officially deprecated.1. As you might guess by name, it replaced the entire state object with all its attributes.)
Remember that calling setState () initiates the execution of render (). In most cases, it works. In some special cases in which the code depends on external data, you can initiate a re-render by calling this.forceUpdate (). Nevertheless, such decisions are undesirable, because relying on external data (instead of state) makes the components less reliable and dependent on external factors (tight binding).
As mentioned earlier, the state object can be accessed in the this.state entry. In JSX, the output values are enclosed in curly braces ({}), therefore, to declare a state property in a view (that is, in the return command of the render method), use the notation {this.state.NAME}.
React magic occurs when you use state data in a view (for example, in output, in an if / else command, as an attribute value or property value of a child), and then pass setState () new values. Bah! React updates all the necessary HTML markup for you. You can verify this in the DevTools console, where the cycles "Updating ..." and "Rendering ..." should be displayed. And the great thing is that this will only affect the absolute minimum required DOM elements.
»More information about the book can be found on the publisher’s website
» Contents
» Excerpt
For Khabrozhiteley 20% discount on coupon - React
Upon payment of the paper version of the book, an electronic version of the book is sent by e-mail.