(Reading this article takes about 2 minutes)

The introduction

As we all know, the most troublesome part of using Redux is the compilation of reducer, because Redux requires that state be immutable, that is, the changed state tree must be a new reference. So reducer is often written like this:

function todoApp(state = initialState, action) {
  switch (action.type) {
    case SET_VISIBILITY_FILTER:
      return Object.assign({}, state, {
        visibilityFilter: action.filter
      })
    case ADD_TODO:
      return Object.assign({}, state, {
        todos: [
          ...state.todos,
          {
            text: action.text,
            completed: false}]})default:
      return state
  }
}
Copy the code

A lot of people would call it deep cloning, but it’s not, it’s neither deep cloning nor shallow cloning.

Correct way to write reducer

If your reducer does deep cloning every Time there is a state change, there is no doubt that your app can work and Time Travelling is also available. What is the problem?

Let’s take a look at the diagram:

The entire state tree is reconstructed, which means that PureComponent and shouldComponentUpdate components that didn’t implement well will be rerendered.

Therefore, Immutable. Js was introduced in the actual project to avoid tedious or incorrect reducer writing. There are also libraries like Immer.

Immutable. Js uses Shared structures internally to avoid deep cloning, which improves Immutable’s own performance and helps React render more efficiently. Something like this:

When a key in an object changes, the value of the other keys in the object does not change, and the object referencing the object generates a new reference, and so on. Thus, our state trees can be compared like value types:

When node 4 changes, nodes 1 and 2 must be unequal before and after the change, but nodes 3, 5 and 6 are still equal without the change. We don’t even use deepEquals to compare references, because Immutable. Js guarantees that they don’t change.

Therefore, our React component will automatically get the best optimization if it uses PureComponent, and components unrelated to changes will not be re-rendered.

Use Immutable. Js with React

In practice, however, we run into a problem. Even with Immutable. Js, many irrelevant components are updated every time. Searching through the code, I found that we now have a number of ways to write:

const mapStateToProps = state= > {
  const user = selectCurrentUser(state)
  const me = user.toJS()
  const myTeam = selectMyTeam(state)
  const team = myTeam && myTeam.toJS()
  / /...
  return { user, me, myTeam, team / *,... * /}}Copy the code

The problem is the toJS call, according to the documentation:

Deeply converts this Keyed collection to equivalent native JavaScript Object.

ToJS will clone the structure shared object completely, and all pureComponents will be rendered again. Take a look at our current situation:

As you can see, when you change the state of a button unrelated to the left sidebar, the left sidebar is still re-rendered.

Here’s what happens when the toJS call is removed:

Isn’t that much better?

conclusion

The render performance of React depends largely on the granularity of updates. When our render function is large enough, we can only do step updates (Fiber and Time Slicing is the main problem to solve) and precise updates.

In order to achieve accurate update, we must deal with state changes. In fact, the simplest way is to flatten the state. The smaller the object level, the less problems we may have in our code. Also, if possible, place connect outside of components that need state. Currently, we still have a lot of components that prematurely connect and pass state layer by layer through props. This is because the state object hierarchy is too deep. The result of. Redux status update (Dispatch) when all Connect(…) Components are updated according to their mapped state, and the earlier a component connects, the more likely it is to be updated, while its children shouldComponentUpdate incorrectly will get a lot of useless updates and lose performance.


References:

  1. Immutable Data Structures and JavaScript
  2. Reducers – Redux
  3. Map — Immutable.js