current position:Home>N methods for optimizing react code

N methods for optimizing react code

2021-08-26 00:41:35 End in sudden wealth

Inventory React Code optimized N Methods

Reference resources :

  1. avoid React Context Duplicate rendering caused by :zhuanlan.zhihu.com/p/50336226
  2. Immutability in React: There’s nothing wrong with mutating objects: blog.logrocket.com/immutabilit…
  3. Immutable.js, Persistent data structures and structure sharing - Why use Immutable.js Not the ordinary JavaScript object ?:medium.com/@dtinth/imm…
  4. Immutable.js How to realize data structure persistence ?: www.cnblogs.com/DM428/p/139…

Catalog

  1. Use React Its own optimization method :PureComponent、shouldComponentUpdate、memo、useMemo、useCallback etc.
  2. React There are several uses that affect performance :
  1. Misuse context Cause all subcomponents re-render, Whether or not context The consumer ;
  2. because React By default, the component is based on shallow comparison props To decide whether to update the policy , reference-type props It will make the child component update unnecessarily with the parent component ;
  3. stay React Component lifecycle methods and React Outside the event callback , Multiple calls setState, Lead to setState Can't be batched , Cause multiple re renderings
  1. Immutable data ,immutable.jsimmer
  2. Reduce double counting :reselector

Text

1. Use React Its own optimization method

drive React The sources of component updates are as follows :

  • Oneself state Update
  • Update of parent component ( No matter props Are there any updates , As long as the parent component is updated , The current component will also be forced to update )
  • props to update
  • forceUpdate Force update

In a class component , By default shouldComponentUpdate return true, As long as the above four points happen , Components will be updated . The first three points may lead to unnecessary updates , To avoid unnecessary updates , have access to shouldComponentUpdate Compare the old and new state And old and new props To determine whether the component should be updated , Easier to use PureComponent, Its internal shouldComponentUpdate It's just simple and shallow state and props ( So when state/props When it's the object , If we want to update components , The original value must be overwritten with a new object , Otherwise, the shallow comparison will cause the component to be unable to update ). stay React Similar optimization methods are used in memo Package components , This can be used in both class components and function components , You can also customize the comparison logic . The above methods are used in the current component to prevent invalid updates , On the parent component, we can also use some methods to prevent invalid updates of the current component , This is mainly through caching props Come on Realization , The method used is useMemo、useCallback The parcel props Then pass it to the sub components .

2. React There are several uses that affect performance

  1. Misuse context May cause all subcomponents re-render, Whether or not context The consumer

    Use context The antipattern of :context Subcomponents and context value The updates are placed in a component , Lead to context value update , Let all Consumer component and Non consumer components Indifference update

    codesandbox.io/embed/jolly…

    Use... Correctly context: hold context value The management and consumption components are separated , stay context value When it's updated , Only consumer components will be updated , Do not let non consumer components update

    codesandbox.io/embed/aged-…

    It is also confirmed from the above two examples , If you don't optimize , The child component will be fully updated with the update of the parent component , thus , In addition to context value Management and context Separate consumer components , We can also package consumer components React.memo() , To avoid invalid updates to consumer components .

  2. because React By default, the component is based on shallow comparison props To decide whether to update the policy , reference-type props It will make the child component update unnecessarily with the parent component

    In a class component , To prevent this , It should be avoided in render Method to create a reference type props And pass it to the subcomponent , Otherwise, every time the parent component re renders , reference-type props Will be a new value , Causes sub components to be re rendered . To avoid the render Create a new reference , For example, don't be in props Pass inline functions in , The callback function should be initialized on the properties of the class component , It's best to use arrow function syntax ( binding this, Look at this article ).

    And in the function component , It can be used useMemo、useCallback The parcel props Then pass it to the sub components , these hooks Will remember the calculated value , The result of the last calculation can be used every time the component is rendered , Of course, if the result of the calculation is an object , Its reference will not change , Therefore, it will not cause sub components to re render .

  3. stay React Component lifecycle methods and React Outside the event callback , Multiple calls setState, Lead to setState Can't be batched , Cause multiple re renderings

    We know setState It could be asynchronous , This asynchrony does not mean setState It's asynchronous , But rather setState The effect is asynchronous , That is to say state The update of is asynchronous ,React Of setState In the same batch transaction, it will be merged into one update , This improves React Performance of . The following situations will setState Batch into one :

    • Multiple setState In the same React Execute... In the composite event callback
    • Multiple setState In the same React Execute in life cycle
    export default function App() {
      const [a, setA] = useState(0);
      const [b, setB] = useState(0);
      const [c, setC] = useState(0);
      const handleClick = () => {
        setA((a) => a + 1);
        setB((b) => b + 2);
        setC((c) => c + 3);
      };
      console.log("render");
      return (
        <div className="App"> <h1>Hello CodeSandbox</h1> <button onClick={handleClick}>click</button> <h2> {a}-{b}-{c} </h2> </div>
      );
    }
     Copy code 

    But sometimes setState The effect may also occur synchronously , If setState Not really React Call in composite event , For example, call... Multiple times in the following cases setState, that state Your updates are synchronized , Can't be batched , Components change with each time setState to update :

    • DOM Native event callback
    • Asynchronous callback , promise.then() 、setTimeout
    export default function App() {
      const [a, setA] = useState(0);
      const [b, setB] = useState(0);
      const [c, setC] = useState(0);
      const handleClick = () => {
        setTimeout(() => {
          setA((a) => a + 1);
          setB((b) => b + 2);
          setC((c) => c + 3);
        }, 0);
    	/* Promise.resolve().then(() => { setA((a) => a + 1); setB((b) => b + 2); setC((c) => c + 3); }); */
      };
      console.log("render");
      return (
        <div className="App"> <h1>Hello CodeSandbox</h1> <button onClick={handleClick}>click</button> <h2> {a}-{b}-{c} </h2> </div>
      );
    }
     Copy code 

    In this case , We can change state Structure , Will be multiple state Synthesize once , And minimize setState The number of times

    React 18 after , All items will be batched by default setState, Whether it's React Composite event callback inside , still setTimeout Wait for asynchronous callback . But there are some cases , If we still need to prohibit mass production ,React There is also a API ReactDOM.fluchSyc(() ⇒ {setState...}) You can skip batch processing

3. Immutable data

Immutable data is , When you try to change the value of a variable , He will create a new variable instead of the original variable , Instead of directly modifying the original variable , The original variable value is immutable .

Immutable data is part of functional programming

This for React Especially important . React We are not allowed to modify state There is a reason , Of course, this is also caused by its own design , Do not directly modify state It can make React By comparing the old and the new state To decide whether to update components , If state Is a deeply nested object , Only deep recursive comparison , This will consume the performance of the application , It also brings inconvenience to developers . If only shallow comparison is allowed ( use ===!== Operator ) It will be more efficient to know whether the component should be updated . So we are React Update in the component state That's what I wrote when I was :


state = {
    user: {
      name:  'chege',
      age: 24,
      friend: {
        name: 'xiaoming',
        age: 22
     }
  }
}
addAge() {
  // spread operator ... 
	this.setState({
    user: {
      ...this.state.user,
      age: 25
    }
   })
  /*  perhaps  Object.assign this.setState( Object.assign({}, this.state.user, {age: 25}) ) */
}

 Copy code 

... and Object.assign() Meeting Shallow copy object , Old and new objects will only reference different objects at the first level , References to deep objects remain the same . This ensures that components that depend on shallow objects can be updated efficiently at the same time , It also avoids unnecessary updates to components that rely on deep objects , Because we just changed the shallow objects , The deep object has not changed .

stay Redux in ,reducer It also requires a pure function , Do not modify state, The reason is the same as React equally , For easier judgment state Whether to update :

Do not modify state. Use Object.assign() Create a new copy . You can't use Object.assign(state, { visibilityFilter: action.filter }), Because it changes the value of the first parameter . You have to set the first parameter to an empty object . You can also turn on ES7 Proposal object expansion operator support , To use { ...state, ...newState } To achieve the same purpose .

But if state The object is a little deeper , We may have to write a lot ... and Object.assign() , This will cause the code to look bad :

state = {
    user: {
    name:  'chege',
    age: 24,
    friend: {
      name: 'xiaoming',
      age: 22
    }
  }
}
addFriendAge() {
  // spread operator ... 
	this.setState({
          user: {
	    ...this.state.user,
           friend: {
             ...this.state.user.friend,
             age: 23
	   }
        }
      })
  /*  perhaps  Object.assign this.setState( Object.assign( {}, Object.assign( {}, this.state.user, ), {age: 25} ) ) */
}

 Copy code 

If you want to balance code aesthetics and application performance , We can use some third-party libraries , They create a kind of Persistent data structures , When creating immutable objects , Ability to reuse old object parts that do not need to be changed , Save time and space for creating objects .

Look at the light copy above ... and Object.assign(), Although we have achieved what makes us React The application is running normally , And achieve the purpose of optimization to a certain extent , But it's not the best way , Besides the ugly code , Also, shallow copies still consume some performance , When the copied object contains 100000 When there are two attributes ,JavaScript Have to copy every attribute honestly , If the attribute is a basic type , A copy of the value of the basic type . So shallow copy will still waste memory and program running time . One can be used not only for React The optimized data immutability implementation scheme is Persistent data structures (Persistent data structures), Its purpose is to create an immutable data , Each change will return a new version , Instead of directly modifying the original data , At the same time, in order to save memory and modification time , Reuse unchanged parts , This is it. Structure of Shared (structural sharing).

There are many realizations Persistent data structures and Structure of Shared The plan , There is no expansion here , Just talk about js Methods used in several libraries that implement immutable data :

  • Immutable.js: Its persistent data structure is based on Prefix tree (trie, Also called dictionary tree ), At the node of the tree 0 and 1 To the left 、 Right node , The path connected from top to bottom nodes path It means key , Leaf nodes represent key values , Immutability is achieved through the copy path , For example, you want to change a leaf node (path by 101, The value is 12), Just copy the nodes on that path , The next node shares . Each modification will produce a new path like the one on the right , But the structure of the tree has not changed , The new path and the original unchanged node can also form a new tree , And the structure is the same , Realize the sharing of unmodified parts . This is the general principle . Some more details are , Handle key hash Numerical value , The value is converted to binary , Mapping to key values through bit partitions as keys , More details can be found here :

    Immutable.js, Persistent data structures and structure sharing - Why use Immutable.js Not the ordinary JavaScript object ?

    Immutable.js How to realize data structure persistence ?

  • immer.js: Did not implement its own data persistence structure , Only by proxy To hijack data modification , Two copies of data before and after modification are maintained internally , The data structure used is also js The original object of , When there is a large amount of data, it will consume a lot of performance .immer.js The advantages of api Less , Easy to use .

Use these libraries , We can modify the data directly , When it returns, it is a new data , The original data will not be modified , At the same time, the first and second data will only break the reference in the modified part , The unmodified part will share the reference .

use immer.js modify state, Simple and clear :

import produce from "immer"

state = {
    user: {
	name:  'chege',
        age: 24,
        friend: {
	 name: 'xiaoming',
         age: 22
       }
   }
}
addFriendAge() {
  this.setState(produce(draft => {
     draft.user.firend.age= 23 
  }))
}
 Copy code 

We can test and use immer.js Can you share the unchanged part :

import produce from "immer";

const state = {
  user: {
    name: "chege",
    age: 24,
    friend1: {
      name: "xiaoming",
      age: 22
    },
    friend2: {
      name: "xiaoming",
      age: 22
    }
  }
};

const nextState = produce(state, (draftState) => {
  draftState.user.friend1.age = 23;
});

console.log(nextState.user === state.user); // false,user  Belong to the changed  path  A node of 
console.log(nextState.user.friend1 === state.user.friend1); // false, friend1  Belong to the changed  path  A node of 
console.log(nextState.user.friend2 === state.user.friend2); 
// true, Shared objects for change ,friend2  Does not belong to the changed  path  A node of , Is the part that should be shared 
 Copy code 

4. Reduce double counting

Redux Medium state All on a tree , When a component subscribes to a state When , You need to deconstruct from the top store, From the top to the next level, and finally to the target state, There may also be some calculations to generate derived data , Used eventually in UI Come out of reality . When store When it's updated , All subscribed state All components will receive update notifications , Recalculate respective subscription data , Even if the subscribed part is not updated , Or updated, the final calculation result was the same at that time , Will be recalculated :

In the following example , When we click add a when ,ConnectChildA and ConnectChildB Of mapStateToProps It will be carried out , Even though ConnectChildB Subscribe to the state No updates , But they all depend on one store, So they all received update notifications , All depend on store All components have been fully updated .

codesandbox.io/embed/confi…

because redux The design of the ,store Any next state All the following components will be fully updated , This is inevitable , However, it can avoid too many invalid calculations during update . stay mapStateToProps This node caches the calculation results , Avoid unnecessary updates to components .reselector Please refer to :

GitHub - reduxjs/reselect: Selector library for Redux

copyright notice
author[End in sudden wealth],Please bring the original link to reprint, thank you.
https://en.qdmana.com/2021/08/20210826004127138b.html

Random recommended