preface

How many of your React projects ignore react. memo, useMemo, and useCallBack at all, resulting in a large number of page data clearly unchanged, but constantly updated.

I used to be in The Chengdu Didi project, this useless render number, each sub-component add up, ordinary page is at least 20 times start, complex page, estimated 60 useless refresh is not too much, plus the browser page itself redraw and reflux, this front-end page is not garbage?

Now someone will say in two voices:

  • The react. memo is difficult to use. It defaults to shallow comparisons, which are useless. Or write their own comparison function is too troublesome, business is too urgent, too lazy to write. And complicated data, it’s harder.
  • Others would say that would be immutable. Js, which is hard to use and too intrusive for old projects.

Explain to the new guy why there are too many renders

The most typical case:

const ChildComponent = React.memo(({ changeText }) = > {
  console.log('Child component executed');
  return (
      <p>test</p>)});const ParentComponent = () = > {
  const [number, setNumber] = useState<number> (1);

  const handleChange = () = > {
    setNumber(number + 1);
  };

  const changeText = (newText) = > {
    setText(newText);
  };

  return (
    <>
      <button onClick={handleChange}>click me</button>
      <p>count: {number}</p>
      <ChildComponent changeText={changeText}  />
    </>)};Copy the code

ChildComponent renders every time the button click me is clicked. Why? The reason is that changeText is a reference type, and every time you setNumber, a reference is created again.

This is similar to what, do you often write style like this

< component A style={{xx attributes}}>Copy the code

And every time you create a new object, it’s the same thing, so these people, they’re all interns.

Okay, one subcomponent rendering once more is fine, but your project is bound to have N components nested, right? If you have enough components nested, enough components on the page, maybe the more useless renders you have?

Pain points for Redux and useState

A super bad store used by Redux is to return new state every time. Why? First of all, redux should not be directly used in the project, because it is too difficult to use (there are many template codes, and it is annoying to switch files back and forth). Dva is suggested. Source code analysis address: juejin.cn/post/708144…

Next, let’s talk about the use of REUDx, which is also the problem of useState. For example, your Reducer should be written in this way

import * as constant from '.. /configs/action';

const initialState = {
    number: 0};export default (state = initialState, action) => {
    switch (action.type) {
        case constant.INCREMENT:
            return {
                ...state,
                number: state.number + 1};case constant.DECREMENT:
            return {
                ...state,
                number: state.number - 1};case constant.CLEAR_NUM:
            return {
                ...state,
                number: 0};default:
            returnstate; }};Copy the code

Here’s the key:

{
   ...state,
   number: state.number + 1,}Copy the code

First the state has to copy itself shallow, then the property has to be overridden later, and when you encounter nested objects, it’s disgusting to write.

useState({ a: 1, b:{ c:1 } })

As mentioned above, useState is the same.

solution

React.memo, useMemo, useCallBack + immer (with immerHooks)

Immer is a library of persistent data structures written by mobx authors, which is much less intrusive than immutable.

So let’s talk about how immer works

In the figure above, we look from left to right, first on the far left is a data structure, similar

const a = {
    b: {
        c: 1,
        d: 1,
        f: 1
    }
    g: { h: { i: 1 } } 
}
Copy the code

We changed the property A.G.H, just as the red part on the far left was changed. Immer will then re-generate the changed chain, as shown in green, and reference the unchanged data in blue.

First, let’s see how redux and useStae can nest multiple layers of objects

Let’s take hooks, useImmerReducer

import React from "react";
import { useImmerReducer } from "use-immer";
 
const initialState = { count: 0 };
 
function reducer(draft, action) {
  switch (action.type) {
    case "reset":
      return initialState;
    case "increment":
      return draft.count++;
    case "decrement":
      returndraft.count--; }}function Counter() {
  const [state, dispatch] = useImmerReducer(reducer, initialState);
  return (
    <>
      Count: {state.count}
      <button onClick={()= > dispatch({ type: "reset" })}>Reset</button>
      <button onClick={()= > dispatch({ type: "increment" })}>+</button>
      <button onClick={()= > dispatch({ type: "decrement" })}>-</button>
    </>
  );
}
Copy the code

This article is mainly to remind those who do not do react basic optimization, wake up petrel!!

As in the code above, when you dispatch, draft is a proxy object, and when you draft.count++, just like the red node we had before, immer has done the transformation for you to generate a new object, just like the green node + blue node in the previous image.

Can immer alone solve the problem?

And the answer is no. Remember the case we talked about earlier? Here it is

const ChildComponent = React.memo(({ changeText }) = > {
  console.log('Child component executed');
  return (
      <p>test</p>)});const ParentComponent = () = > {
  const [number, setNumber] = useState<number> (1);

  const handleChange = () = > {
    setNumber(number + 1);
  };

  const changeText = (newText) = > {
    setText(newText);
  };

  return (
    <>
      <button onClick={handleChange}>click me</button>
      <p>count: {number}</p>
      <ChildComponent changeText={changeText}  />
    </>)};Copy the code

UseCallBack is used to persist function data as follows:

const changeText = useCallback((newText) => { setText(newText); } []);Copy the code

Others may require useMemo, and then almost all components are wrapped in a layer of React.Memo.

End of this article!