📜 ⬆️ ⬇️

Optimize redux storage for more productive changes.

This post is a continuation of the post about optimizing the list performance in React application .

Attention. In this post, examples are prepared specifically for Redux applications. But the approach itself can be applied with other libraries. Also, the following tip works in react-redux version 5 . I could not achieve the desired result in version 4 . Deeply understand the reasons I did not.

And so, the standard way to store some set of elements in an application is to store them in an array:
')
const state = { targets: [{id: 'target1', radius: 10}, {id: 'target2', radius: 2}] }; 

Further, most likely, somewhere in your application there will be a component that displays this list:

 const TargetsList = connect(state => ({targets: state.targets}))( ({ targets }) => <ul> { targets.map((target) => <TargetView key={target.id} target={target} />) } </ul> ); 

If suddenly you need to update one element, then you have to update the entire array (and you need to):

 function appReducer(state, action) { if (action.type === 'UPDATE') { return { target: state.target.map((target) => { if (target.id === action.id) { return { ...target, radius: action.radius }; } else { return target; } }) } } // some other code } 

Also, updating the element will update the entire TargetsList view (its render will be called).

I made a small demo with a test code for measuring performance.

On my machine, updating one item in the list with a length of 1000 takes about 21ms . In the previous post, I described a way to improve performance with additional subscriptions to state changes in the child component, as well as adding some logic to the “shouldComponentUpdate” for the component that displays the list.

But almost the same result can be achieved by a small change in the form of the data.

How to optimize?


If you use github.com/reactjs/react-redux , then you can increase performance by changing the state form to:

 const state = { targetsOrder: ['id-1', 'id-2'], targets: { 'id-1': { id: 'id-1', radius: 10 }, 'id-2': { id: 'id-2', radius: 20 }, } }; 

Next, you need to slightly change the “TargetsList” component:

 const TargetsList = connect(state => ({targetsOrder: state.targetsOrder}))( ({ targetsOrder }) => <ul> { targetsOrder.map((id) => <TargetView key={id} targetId={id} />) } </ul> ); 

Notice that, in this case, I am passing the ID of the element to the child component, not the entire element. Then “TargetView” cannot be a “stupid component” and must subscribe to changes in the state:

 const TargetView = connect( (state, ownProps) => ({target: state.targets[ownProps.targetId]}) )(({ target }) => { // your render logic return <Circle radius={target.radius} />; }); 

Since TargetView is subscribed to status changes, it will update itself when its data is updated. It is important that the “TargetList” WILL NOT be updated when the item in the list changes because the “targetsOrder” remains the same. In some cases, this technique can significantly improve performance.

→ Updated demo with measurements

Now updating one item takes 2.2ms on my machine. This is almost 10 times faster than in the previous example.

Source: https://habr.com/ru/post/318268/


All Articles