神刀安全网

Why immutability matters in React

When you start learning React by building an app, at some point you’ll get into trouble because of mutable data. Or maybe someone will tell you that you should have everything immutable. But why?

Where’s the problem?

Let’s imagine that you built a large application in React with lots of forms and a deep tree of components. You should know that, in React, when a component gets updated it will trigger an update of its child components. Re-rendering propagates down the components tree, meaning that the whole application gets re-rendered on every change made to the state of the app. Even that React will not actually change the DOM, it will still compute VDOM and do a diff. That’s a lot of work. And you’ll see how the UI gets slow, especially in a reasonably large application. This is why a special method is introduced in React’s component API.

Why immutability matters in React

shouldComponentUpdate (SCU) is responsible for cutting off updates propagation. But React components don’t implement it by default. It is the developer’s responsibility to decide if an application requires more performance optimisation. In this method, you would normally check if the state or the props of the component has changed, and in case they didn’t you would return false , so the component and its child components will not re-render. This is clearly shown in the above illustration. The problem starts here: if the data is mutable and it was mutated directly, SCU will always return false which means that components will never re-render, hence you’ll never see any changes in the UI.

Let’s see some code:

// Initial state const state = {     contacts: [     {       name: 'John'     },     {       name: 'Mark'     }   ] };  // State mutation function function setName(state, idx, name) {     state.contacts[idx].name = name;   return state; }  // Change the first contact and get a new state const nextState = setName(state, 0, 'Miley');   

In a context of a React component, we want to set its state to nextState and re-render only the first contact which is the only one that has been changed. To achieve this optimisation we would want to setup proper SCU check using shallow-equals module. It provides a function to compare objects shallowly, which means it doesn’t do deep comparison. Instead, it compares only top level properties. This allows us to compare properties of state and props to find out if any of them has been changed.

shouldComponentUpdate(nextProps, nextState) {     return !shallowEqual(nextState, this.state) ||          !shallowEqual(nextProps, this.props); } 

But this check will fail because both nextState and this.state are equal. This happens because the state was mutated directly, so both next and current state are pointing to the exact same value. If you are not sure, try to evaluate the above snippet in your browser’s console and inspect both state and nextState variables. You can even compare both and the result will be true , which means that both variables are pointing to the same value.

Looks like we are stuck between two problems: the whole app may be slow without this optimisation, but if SCU is implemented without having immutable data around, the app will not update at all.

Solution

There are two possible ways: use a library for immutable data structures ( Immutable or mori ) or learn how to make changes that an existing data doesn’t change.

Learning and integrating a library is an expensive solution, because it requires time. Before using it, think what benefits it would give to your project.

Making changes in an immutable way requires to write more code. But you can always extract common operations into a small lib. This is probably a good solution when you don’t have a huge dataset. Because you don’t really get much benefit of persistent data structures on a small amount of data.

Let’s check the above example with contacts and figure out how to play immutable game with data.

// Initial state const state = {     contacts: [     {       name: 'John'     },     {       name: 'Mark'     }   ] };  // State mutation function function setName(state, idx, name) {    // Copy an object shallowly   const nextState = Object.assign({}, state);    // Perform changes   nextState.contacts = nextState.contacts.map((contact, i) => {     if (i === idx) {       return { name };     }     return contact;   });    return nextState; }  // Change the first contact and get a new state const nextState = setName(state, 0, 'Miley');  // state [{ name: 'John' }, { name: 'Mark' }] // nextState [{ name: 'Miley' }, { name: 'Mark' }] 

Now that both state snapshots are different, SCU check will prevent unchanged data from re-rendering. However setName function became less straightforward. Why are we creating objects and mapping over an array here?

We want to copy a state object including a change, but we don’t need to clone everything. An unchanged value could be assigned be reference and thus reused. In the code above we create a new array of contacts with map and populate it with objects from the initial state and the one which represents a change. This way your application will consume less memory and make it possible to optimise rendering with SCU because of immutable data.

Conclusion

At some point you will feel that your app is getting slow. This is a strong sign that you should implement rendering optimisation with shouldComponentUpdate where it is appropriate. To make it work, you need to learn immutability. At first glance it looks like an overhead, but keep in mind that your app will get instant performance boost. Also, immutability is not just about React and its rendering performance. By having immutable data, you don’t need to worry about potentially breaking some parts of the program when mutating values on which these parts are dependant.

Here’s a recipe for you to take home:

  • Learn and start using immutability from the very beginning of the project. You’ll get into trouble without it very soon.
  • If you have a lot of data, use libraries for persistent data structures. If not, just copy objects as shown in this article.
  • Don’t be scared of memory consumption, unless you are developing UIs with really large amount of data.

Make sure you read the Advanced Performance section in the official docs.

转载本站任何文章请注明:转载至神刀安全网,谢谢神刀安全网 » Why immutability matters in React

分享到:更多 ()

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址