Long lists in react that can't be paginated can cause expensive re-renders reacting to a small change, rendering the whole application obsolete, get it :)?
In this post, we will explore that problem using a simple todo app with a very long list of todos for demonstration purposes. The knowledge can be applied to any similar situation not limited to todo apps.
The problem
Let’s assume a to-do list with 1000 items. And each item has a completed status and toggle.
N - not complete and
C - completed
Now let’s toggle item 3's completed status,
The whole todo item component is re-rendered even though only item 3 is changed. This will cause a major lag. The effect is noticeable even for simple text-based list.
The solution
Let's introduce reducer to handle the state,
const [state, dispatch] = useReducer(todoReducer, initialState);
dispatch
doesn’t change between state changes. So we can leverage that to avoid re-renders. We basically declare our context state as above.
Our toggle function in context. Here we will have to wrap the function with useCallback and provide only dispatch as a dependency. By doing this we make sure the function isn’t re-created every time the state is changed. And it will help when passing the function as a prop.
const toggleCompleted = useCallback(
(id: number) => {
dispatch({ type: "MARK_AS_COMPLETED", payload: id });
},
[dispatch]
);
The only catch here is that we cannot access the latest state in this function. Because it’s never updated with the state.
To overcome that we will have to access our state in the reducer.
else if (action.type === "MARK_AS_COMPLETED") {
// here we can get the latest state
const updated = state.todoList.map((item) => {
if (item.id === action.payload) {
return { ...item, completed: !item.completed };
}
return item;
});
return {
...state,
todoList: updated,
};
}
And the todo item will be wrapped with memo. By doing this we make sure that todo and toggleCompleted
stay the same between re-renders. And memo will be able to avoid the re-rendering of the component.
And when using memo we cannot access a context inside that component. We will have to pass the values as a prop.
export default memo(TodoItem);
That’s it. TodoItem is memorized.
Now let's try toggling item 3.
The result
Only item 3 is re-rendered.
.
.
.
mic drop
Top comments (1)
Very nice.. It is nice if you share github repo