React In-depth series provides in-depth explanations of key concepts, features, and patterns in React to help you understand React and use React flexibly in your projects.

The core idea of React is componentization. The React component can be defined by the following formula:

UI = Component(props, state)
Copy the code

The UI of the component is calculated based on the props and state parameters. As you can see, props and state are two important data sources for the component.

This article is not an introduction to the basic uses of props and state, but an attempt to explain props and state at a deeper level and summarize the considerations when using them.

Props and State nature

Props is the interface to the outside of a component, and state is the interface to the inside of a component. ** Components can reference other components. The references between components form a tree structure (component tree). If a lower-layer component needs to use data or methods from the upper-layer component, the upper-layer component can pass the props property of the lower-layer component, so the props is the interface to the component. In addition to using data passed by upper-layer components, a component may also need to maintain administrative data itself, which is its internal interface state. Based on the external interface props and internal interface state, the component calculates the UI of the corresponding interface.

Both the props and state of the component are directly related to the UI that the component ultimately renders. The main difference between the two is that state is mutable, which is a set of states maintained internally by the component to reflect changes in the component’S UI. The props is a read-only attribute of a component. To change the props, you can only change the props on the upper layer of the component. In a scenario where component state moves up, it is the parent component that passes the required state to the child component through the props of the child component.

How to define State

Defining an appropriate state is the first step to properly creating a component. State must represent the complete set of states presented by a component UI, that is, any change of the component’s CORRESPONDING UI can be reflected from the change of state; At the same time, state must also be the minimum set of states to represent a component’S UI, that is, all states in state are used to reflect changes in the component’s UI, without any redundant states or intermediate states that need to be computed from other states.

There are four criteria for determining whether a variable used in a component should be used as component state:

  1. Is this variable fetched from the parent component via props? If it is, then it is not a state.
  2. Does this variable remain constant throughout the life of the component? If it is, then it is not a state.
  3. Can this variable be evaluated from existing data in props or state? If it is, then it is not a state.
  4. Is this variable used in the render method of the component? If not, then it’s not a state. In this case, it is better to define this variable as a general component property (component properties other than props and state). For example, a timer used in a component should be defined as this.timer instead of this.state.timer.

Keep in mind that not all variables used in a component are state of the component! When there are multiple components that depend on the same state, it is common practice to move the state up and place the state in the common parent of each component.

How do I change State correctly

1. State cannot be modified directly.

If you change state directly, the component does not re-render. Such as:

// Error this.state.title ='React';
Copy the code

The correct way to do this is to use setState():

/ / this. Correct setState ({title:'React'});
Copy the code
2. State updates are asynchronous.

When setState is called, the component’s state does not change immediately. SetState simply puts the state to be changed into a queue. React optimizes the actual execution timing, and React may merge multiple setState changes into one state change for performance reasons. So you can’t rely on the current state to calculate the next state. When a state change is actually performed, the this.state dependency is not guaranteed to be the latest state because React will merge multiple state changes into one. In this case, this.state will still be the same as the state before the changes occurred. Also, you can’t rely on the current props to calculate the next state, because updates to the props are also asynchronous.

For example, for an e-commerce application, in our shopping cart, when we click the buy button once, the number of purchases increases by 1. If we click the button twice in a row, this. SetState ({quantity:) is called twice in a row. This.state. quantity + 1}), the React merge is equivalent to the following code:

Object.assign(
  previousState,
  {quantity: this.state.quantity + 1},
  {quantity: this.state.quantity + 1}
)
Copy the code

As a result, the later operation overwrites the previous one, and the final purchase increases by only 1.

If you really need this, you can use another setState that takes a function as an argument. This function takes two arguments. The first argument is the previous state of the component (the state before the modification was successful), and the second argument is the latest props of the component. As follows:

This.setstate ((preState, props) => ({counter: prestate. quantity + 1; }))Copy the code
3. State update is a Shallow Merge process.

When setState is called to modify the state of a component, only the changed state variable is passed in, not the full state of the component, because updating the component state is a Shallow Merge. For example, a component’s state is:

this.state = {
  title : 'React',
  content : 'React is an wonderful JS library! '
}
Copy the code

When only the state title needs to be changed, only the modified title needs to be passed to setState:

this.setState({title: 'Reactjs'});
Copy the code

React merges the new title into the original component state while retaining the original content state.

{
  title : 'Reactjs',
  content : 'React is an wonderful JS library! '
}
Copy the code

The State and Immutable

React recommends treating state as an immutable object. On the one hand, if you change this.state directly, the component will not be rerendered. On the other hand, all states contained in state should be immutable objects. When a state in state changes, we should create a new state instead of directly modifying the original state. So how do you create a new state when it changes? According to the type of state, there are three cases:

1. State types are immutable (numbers, strings, Booleans, null, undefined)

This is the easiest case because the state is an immutable type and you simply assign a new value to the state you want to change. If you want to change the status of count (numeric type), title (string type), and SUCCESS (Boolean type) :

this.setState({
  count: 1,
  title: 'Redux',
  success: true
})
Copy the code
2. The state type is array

If you have a state books of array type, use the concat method of array or ES6’s spread syntax when adding a book to books:

This.setstate (preState => ({books: prestate.books.concat (['React Guide']); SetState => ({books: [... prestate.books, '... prestate.books, ');'React Guide'];
}))
Copy the code

When a partial element is truncated from books as a new state, use the slice method of arrays:

This.setstate (preState => ({books: prestate.books.slice (1,3); }))Copy the code

When some elements are filtered from books as new states, use the filter method of the array:

This.setstate (preState => ({books: prestate.books.filter (item => {returnitem ! ='React'; 
  });
}))
Copy the code

Do not use push, POP, Shift, unshift, splice, etc., to change the state of the array type, because these methods are based on the original array, while concat, slice, filter return a new array.

3. The type of state is Plain Object

If state has a state owner, the structure is as follows:

this.state = {
  owner = {
    name: 'Veteran cadre',
    age: 30
  }  
}
Copy the code

When modifying state, there are two ways:

1) Use ES6’s Object.assgin method

this.setState(preState => ({
  owner: Object.assign({}, preState.owner, {name: 'Jason'});
}))
Copy the code

2) Use object extension syntax (object spread properties)

this.setState(preState => ({ owner: {... preState.owner, name:'Jason'};
}))
Copy the code

To summarize, the key to creating a new state is to avoid methods that directly modify the original object and instead use methods that return a new object. Of course, you can use some Immutable JS libraries, such as Immutable. JS, to achieve a similar effect.

So why does React recommend that the component’s state be immutable? On the one hand, immutable objects are easy to manage and debug. See here for more information. On the other hand, for the sake of performance, when component states are immutable objects, we in the component shouldComponentUpdate method, only need to compare the reference of the state can determine whether the state really changed, so as to avoid unnecessary render method call. When we use the React PureComponent, we need to ensure that the component state is immutable, otherwise the state comparison in the component’s shouldComponentUpdate method can be wrong.

The next preview:

React In-depth Series 4: Component lifecycle


My new book, The React Road, is out now. If you’re interested in React, check it out. Purchase address: Dangdang JINGdong


Welcome to pay attention to my public number: the big front end of the old cadre