React has been in use for three years, and there are a number of best practices on React code optimization that have been developed over the past three years. See if the article is popular and then decide whether to share the following.

For each best practice in this article I will provide two examples, one good and one bad for comparison, and provide a.gif image preview.

This article mainly optimizes the following three situations:

  • Parent component updates cause child components to render
  • Incorrect writing of Props causes the component to render
  • Updating the Context causes the component to render

After reading the article, if you think it is helpful to you, please give me a thumbs-up. Your thumbs-up is the biggest motivation for my creation. Comment like can get the source code!!

Parent component updates cause child components to render

The Class example

❎ Error example preview

❎ Error Example

import React, { Component } from "react"; class Parent extends Component { constructor(props) { super(props); this.state = { count: 0, }; } handleClick = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; render() { const { count } = this.state; Return (<div className="parent"> <h5> <p> Count--{Count}</p> <button onClick={this.handleClick}> add </button> <Son /> </div> ); } } class Son extends Component { constructor(props) { super(props); } render() {console.log(" Child component rerender!!" ); Return <div className="son"> subcomponent </div>; } } export { Parent, Son };Copy the code

βœ‹πŸ» Click to view the online demo

In this example, the state change in the parent component causes the child component to be re-rendered. This is a normal way to write code, but seriously, it is a waste of performance because the child component is re-rendered! Let’s see how to solve this problem!

Note: this example does not mean to eliminate writing such code, in fact, optimization also depends on the scene!!

βœ… Correct example 1

import React, { Component, PureComponent } from "react"; class Parent extends Component { constructor(props) { super(props); this.state = { count: 0, }; } handleClick = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; render() { const { count } = this.state; Return (<div className="parent"> <h5> <p> parent Count--{Count}</p> <button onClick={this.handleClick}> add </button>  <Son /> </div> ); } } class Son extends PureComponent { constructor(props) { super(props); } render() {console.log(" Child component rerender!!" ); Return <div className="son"> subcomponent </div>; } } export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example we are borrowing the PureComponent inheritance class. React will automatically perform shouldComponentUpdate for shallow optimization of the Props.

React. CreateElement (Son) {React.createElement(Son) {React.createElement(Son) {React.createElement(Son) {React.

βœ… Correct example 2

import React, { Component } from "react"; class Parent extends Component { constructor(props) { super(props); this.state = { count: 0, }; } handleClick = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; render() { const { count } = this.state; const { children } = this.props; Return (<div className="parent"> <h5> <p> parent Count--{Count}</p> <button onClick={this.handleClick}> add </button>  {children} </div> ); } } export default Parent; <Parent> <Son /> </Parent>Copy the code

βœ‹πŸ» Click to view the online demo

In the optimization of this example, we separate the stateful component from the stateless component, passing the stateless component in with children. This will avoid meaningless repeated rendering! So why does this avoid rerendering? React. CreateElement (Son) is used to render children in the React state component. This can also be optimized!

βœ… Correct example 3

import React, { Component, memo } from "react"; import { Son } from "./Bad"; const MemoSon = memo(() => <Son></Son>); class Parent extends Component { constructor(props) { super(props); this.state = { count: 0, }; } handleClick = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; render() { const { count } = this.state; Return (<div className="parent"> <h5> <p> parent Count--{Count}</p> <button onClick={this.handleClick}> add </button>  <MemoSon /> </div> ); } } export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example, the optimization idea is similar to the one mentioned in Example 1. We borrowed the Memo Function, which is actually an optimization tool for the Function component. We are also here brazenly forced use!! The idea of avoiding re-rendering is also a reference to contrast Props. Decide to render or not!!

βœ… Correct example 4

import React, { Component, useState, Fragment } from "react"; import { Son } from "./Bad"; const ClickCount = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount((old) => old + 1); }; </h5> <p> Parent component Count--{Count}</p> <button onClick={handleClick}> Add </button> </div> </Fragment> ); }; class Parent extends Component { constructor(props) { super(props); } render() { return ( <div className="parent"> <ClickCount /> <Son /> </div> ); } } export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example, our optimization approach is to isolate the state component as a component, so that the state changes are separated from the child components. Rerendering of child components can also be avoided!!

This optimization means to speak seriously or use very little, see the situation use it!!

Hooks sample

Error example preview

❎ Error Example

import { useState } from "react"; Const Son = () => {console.log(" Child component rerendered!! ") ); Return <div className="son"> subcomponent </div>; }; const Parent = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount((old) => old + 1); }; Return (<div className="parent"> <h5> <p> Parent component Count--{Count}</p> <button onClick={handleClick}> add </button> <Son /> </div> ); }; export { Son, Parent };Copy the code

βœ‹πŸ» Click to view the online demo

This is also perfectly normal for Hooks, but in contrast to the Class component, the Function component is reexecuted every time the component is re-rendered. For Class components, the new Class is only executed once, which is pretty scary when you think about it. For a function set, each execution means a new context, a new variable, and a new scope. Therefore, we should pay more attention to the performance optimization of functional components.

βœ… Correct example 1

import { useState } from "react"; const Parent = ({ children }) => { const [count, setCount] = useState(0); const handleClick = () => { setCount((old) => old + 1); }; </h5> <p> Parent component Count--{Count}</p> <button onClick={handleClick}> Add </button> {children} </div> ); }; export default Parent; <Parent> <Son /> </ParentCopy the code

βœ‹πŸ» Click to view the online demo

In this example, we use children to render the children directly, as we did in the Class component example above. This optimization also avoids meaningless rendering.

Seriously speaking, combined with the characteristics of the function components of this optimization means is actually palliative!

βœ… Correct example 2

import { useState, useMemo } from "react"; import { Son } from "./Bad"; const Parent = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount((old) => old + 1); }; </h5> <p> Parent component Count--{Count}</p> <button onClick={handleClick}> Add </button> {useMemo( () => ( <Son /> ), [] )} </div> ); }; export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example we use the useMemo optimization Hook, we cache the Son component, and only when the dependency changes, we re-execute the function to complete the re-rendering, otherwise ensuring memoized is the same, which helps to avoid costly calculations every time we render. It also avoids having to redeclare variables, functions, scopes, and so on every time in a child component.

Note: I think this optimization is a stroke of genius because useMemo saves component references and does not re-execute function components, thus avoiding variable, function declarations, and scope declarations within components. This optimizes performance. Nice!!!!!

βœ… Correct example 3

import { useState, memo } from "react"; import { Son } from "./Bad"; const SonMemo = memo(Son); const Parent = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount((old) => old + 1); }; </h5> <p> Parent component Count--{Count}</p> <button onClick={handleClick}> add </button> <SonMemo /> </div> ); }; export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example, we use the Memo API to check if the props reference is changed to avoid re-rendering the subcomponents!

Incorrect writing of Props causes the component to render

The Class example

❎ Error example preview

❎ Error Example

import React, { Component, PureComponent } from "react"; class Parent extends Component { constructor(props) { super(props); this.state = { count: 0, }; } handleClick = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; render() { const { count } = this.state; Return (<div className="parent"> <h5> <p> Count--{Count}</p> <button onClick={this.handleClick}> add </button> < Son componentDetails = {{name: "children"}} anyMethod = {() = > {}} / > < / div >). } } class Son extends PureComponent { constructor(props) { super(props); } render() { const { componentDetails, anyMethod } = this.props; console.log("Son -> render -> anyMethod", anyMethod); console.log("Son -> render -> componentDetails", componentDetails); return <div className="son">{componentDetails? .name}</div>; } } export { Parent, Son };Copy the code

βœ‹πŸ» Click to view the online demo

In this example, the Props pass is written incorrectly. Why? Because the component is rendered primarily by listening for Props and State changes, the Props passed in this example is a new object each time, because each rendering of the parent component results in a rendering of the child component because of the different references. Therefore, the rerendering of real numbers caused by this method should not be!!

So what should we write?

βœ… Correct example 1

import React, { Component, PureComponent } from "react"; class Parent extends Component { constructor(props) { super(props); This. state = {count: 0, componentDetails: {name: "componentDetails"},}; } handleClick = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; anyMethod = () => {}; render() { const { count, componentDetails } = this.state; Return (<div className="parent"> <h5> Correct example 1</h5> <p> Parent Count--{Count}</p> <button OnClick ={this.handleclick}> add </button> <Son componentDetails={componentDetails} anyMethod={this.anymethod} /> </div>); } } class Son extends PureComponent { constructor(props) { super(props); } render() { const { componentDetails, anyMethod } = this.props; console.log("Son -> render -> anyMethod", anyMethod); console.log("Son -> render -> componentDetails", componentDetails); return <div className="son">{componentDetails? .name}</div>; } } export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

The main correct way to write this example is to pass the variable directly to the child component, because the reference to the variable is the same and therefore does not change after PureComponent checks, preventing the child from rendering!!

Note: Strictly speaking, this incorrect example is a writing problem, resulting in the re-rendering of child components, so there is no optimization, so we have to prohibit writing code like the incorrect example!

Hooks sample

❎ Error example preview

❎ Error Example

import { useState, useEffect } from "react"; const Son = ({ componentDetails, anyMethod }) => { useEffect(() => { console.log("Son -> componentDetails", componentDetails); }, [componentDetails]); useEffect(() => { console.log("Son -> anyMethod", anyMethod); }, [anyMethod]); return <div className="son">{componentDetails.name}</div>; }; const Parent = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount((old) => old + 1); }; Return (<div className="parent"> <h5> <p> Parent component Count--{Count}</p> <button onClick={handleClick}> add </button> <Son ComponentDetails = {{name: "children"}} anyMethod = {() = > {}} / > < / div >). }; export { Son, Parent };Copy the code

βœ‹πŸ» Click to view the online demo

In this example, the method for writing props is incorrect. Here’s how to fix it!

βœ… Correct example 1

import { useState, useEffect } from "react"; const Son = ({ componentDetails, anyMethod }) => { useEffect(() => { console.log("Son -> componentDetails", componentDetails); }, [componentDetails]); useEffect(() => { console.log("Son -> anyMethod", anyMethod); }, [anyMethod]); return <div className="son">{componentDetails.name}</div>; }; Const componentDetails = {name: "child"}; // Const componentDetails = {name: "child"}; const anyMethod = () => {}; const Parent = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount((old) => old + 1); }; Return (<div className="parent"> <h5> Correct example 1</h5> <p> Parent component Count--{Count}</p> <button onClick={handleClick}> add </button> <Son  componentDetails={componentDetails} anyMethod={anyMethod} /> </div> ); }; export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example, we simply lift the invariant value out of the component to ensure that the reference is unique and does not change as the component is updated. But there are some limitations to this writing. It only works with values that are constant. But it also effectively avoids duplicate rendering of components.

βœ… Correct example 2

import { useState, useEffect, useMemo, useCallback } from "react"; const Son = ({ componentDetails, anyMethod }) => { useEffect(() => { console.log("Son -> componentDetails", componentDetails); }, [componentDetails]); useEffect(() => { console.log("Son -> anyMethod", anyMethod); }, [anyMethod]); return <div className="son">{componentDetails.name}</div>; }; const Parent = () => { const [count, setCount] = useState(0); const handleClick = () => { setCount((old) => old + 1); }; const anyMethod = useCallback(() => {}, []); Const [componentDetails] = useMemo(() => {const componentDetails = {name: "componentDetails"}; return [componentDetails]; } []); Return (<div className="parent"> <h5> Correct example 2</h5> <p> Parent component Count--{Count}</p> <button onClick={handleClick}> add </button> <Son  componentDetails={componentDetails} anyMethod={anyMethod} /> </div> ); }; export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example, two optimization hooks, useCallback and useMemo, are used to determine whether to update the value change according to whether the dependency changes, so as to ensure that the value reference does not change. This is suitable for most forms of writing, but should not be overused. Otherwise the code would be messy.

Updating the Context causes the component to render

The Class example

❎ Error example preview

❎ Error Example

import React, { Component, createContext } from "react"; const contextValue = createContext(undefined); class Parent extends Component { constructor(props) { super(props); this.state = { count: 0, handleIncrement:this.handleIncrement }; } handleIncrement = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; Render () {return (< contextvalue.provider value={this.state} > <div className="parent"> <h5> Error </h5> <Son1 /> <contextValue.Consumer> {(conProps) => <Son2 conProps={conProps} />} </contextValue.Consumer> </div> </contextValue.Provider> ); } } class Son1 extends Component { constructor(props) { super(props); } render() {console.log(" child component 1 is rerendered!!" ); Return <div className="son"> subcomponent 1</div>; } } class Son2 extends Component { constructor(props) { super(props); } render() {console.log(" child component 2 is rerendered!!" ); const { conProps: { count, handleIncrement }, } = this.props; Return (<div className="son"> <p> subcomponent 2--{count}</p> <button onClick={handleIncrement}> increment </button> </div>); } } export { Parent };Copy the code

βœ‹πŸ» Click to view the online demo

In this example, notice that when you click the button in child component 2, you change the state in the parent component, so the problem is that the parent component’s rendering causes the child component to render. So how do you avoid repeated rendering of child components?

βœ… Correct example 1

import React, { Component, createContext } from "react"; const contextValue = createContext(undefined); class Parent extends Component { constructor(props) { super(props); this.state = { count: 0, handleIncrement:this.handleIncrement }; } handleIncrement = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; render() { const { children } = this.props; Provider value={this.state} > <div className="parent"> <h5> Correct Example 1</h5> {children} <contextValue.Consumer> {(conProps) => <Son2 conProps={conProps} />} </contextValue.Consumer> </div> </contextValue.Provider> ); } } class Son1 extends Component { constructor(props) { super(props); } render() {console.log(" child component 1 is rerendered!!" ); Return <div className="son"> subcomponent 1</div>; } } class Son2 extends Component { constructor(props) { super(props); } render() {console.log(" child component 2 is rerendered!!" ); const { conProps: { count, handleIncrement }, } = this.props; Return (<div className="son"> <p> subcomponent 2--{count}</p> <button onClick={handleIncrement}> increment </button> </div>); } } export { Parent, Son1 }; <Parent> <Son1 /> </Parent>Copy the code

βœ‹πŸ» Click to view the online demo

In this example, we still borrow the children mechanism and render directly, so there is no implementation of the Ract.createElement(Son) API in the parent component, so there is no duplicate rendering!

βœ… Correct example 2

import React, { Component, createContext, PureComponent } from "react"; const contextValue = createContext(undefined); class Parent extends Component { constructor(props) { super(props); this.state = { count: 0, handleIncrement:this.handleIncrement }; } handleIncrement = () => { const { count } = this.state; this.setState({ count: count + 1, }); }; Render () {return (< contextvalue.provider value={this.state} > <div className="parent"> <h5> correct example 2</h5> <Son1 /> <contextValue.Consumer> {(conProps) => <Son2 conProps={conProps} />} </contextValue.Consumer> </div> </contextValue.Provider> ); } } class Son1 extends PureComponent { constructor(props) { super(props); } render() {console.log(" child component 1 is rerendered!!" ); Return <div className="son"> subcomponent 1</div>; } } class Son2 extends PureComponent { constructor(props) { super(props); } render() {console.log(" child component 2 is rerendered!!" ); const { conProps: { count, handleIncrement }, } = this.props; Return (<div className="son"> <p> subcomponent 2--{count}</p> <button onClick={handleIncrement}> increment </button> </div>); } } export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example, we borrowed the PureComponent class to help us automatically optimize and thus avoid repeated rendering.

Note: You can also force the use of react. memo here.

Hooks sample

❎ Error example preview

❎ Error Example

import { createContext, useContext } from "react"; import { useCustomReducer } from ".. /useCustomizeContext"; const CustomizeContext = createContext(undefined); Const Son1 = () => {console.log(" Child component 1 is rerendered!! ") ); Return <div className="son"> subcomponent 1</div>; }; const Son2 = () => { const { count, handleIncrement } = useContext(CustomizeContext); Console. log(" Child component 2 has been rerendered!!" ); Return (<div className="son"> <p> subcomponent 2-{count}</p> <button onClick={handleIncrement}> increment </button> </div>); }; const Parent = () => { const value = useCustomReducer({ initValue: 1 }); Return (< CustomizeContext. The Provider value = {value} > < div className = "parent" > < h5 > wrong example < / h5 > < Son2 / > < Son1 / > < / div > </CustomizeContext.Provider> ); }; export { Son1, Parent, Son2 };Copy the code

βœ‹πŸ» Click to view the online demo

In this example, the createContext, useContext, and useReducer apis are used to implement a small Redux. Clicking on the button in child component 2 changes the value of count, causing value to change, so the parent component renders, causing the child component to render as well.

βœ… Correct example 1

import React from "react"; import { CustomizeProvider, useCustomizeContext, useCustomReducer, } from ".. /useCustomizeContext"; Const Son1 = () => {console.log(" Child component 1 is rerendered!! ") ); Return <div className="son"> subcomponent 1</div>; }; const Son2 = () => { const { count, handleIncrement } = useCustomizeContext(); Console. log(" Child component 2 has been rerendered!!" ); Return (<div className="son"> <p> subcomponent 2-{count}</p> <button onClick={handleIncrement}> increment </button> </div>); }; const Parent = ({ children }) => { const value = useCustomReducer({ initValue: 1 }); Return (<CustomizeProvider value={value}> <div className="parent"> <h5> Correct example 1</h5> <Son2 /> {children} </div> </CustomizeProvider> ); }; export { Son1 }; export default Parent; <Parent> <Son1 /> </Parent>Copy the code

βœ‹πŸ» Click to view the online demo

In this example we are still using children to solve the repeat rendering problem. This method is very effective!!

Note: in fact, in the project must use the appropriate optimization means!

βœ… Correct example 2

import React, { memo } from "react"; import { CustomizeProvider, useCustomizeContext, useCustomReducer, } from ".. /useCustomizeContext"; Const Son1 = () => {console.log(" Child component 1 is rerendered!! ") ); Return <div className="son"> subcomponent 1</div>; }; const Son2 = () => { const { count, handleIncrement } = useCustomizeContext(); Console. log(" Child component 2 has been rerendered!!" ); Return (<div className="son"> <p> subcomponent 2-{count}</p> <button onClick={handleIncrement}> increment </button> </div>); }; // Use memo const MemoSon1 = memo(Son1); const Parent = () => { const value = useCustomReducer({ initValue: 1 }); Return (<CustomizeProvider value={value}> <div className="parent"> <h5> Correct example 2</h5> <Son2 /> <MemoSon1 /> </div> </CustomizeProvider> ); }; export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example, the Memo API is also used, and again, the reference to props is changed to determine whether to update it.

βœ… Correct example 3

import React, { useMemo } from "react"; import { CustomizeProvider, useCustomizeContext, useCustomReducer, } from ".. /useCustomizeContext"; Const Son1 = () => {console.log(" Child component 1 is rerendered!! ") ); Return <div className="son"> subcomponent 1</div>; }; const Son2 = () => { const { count, handleIncrement } = useCustomizeContext(); Console. log(" Child component 2 has been rerendered!!" ); Return (<div className="son"> <p> subcomponent 2-{count}</p> <button onClick={handleIncrement}> increment </button> </div>); }; const Parent = () => { const value = useCustomReducer({ initValue: 1 }); Return (<CustomizeProvider value={value}> <div className="parent"> <h5> Correct example 3</h5> <Son2 /> {useMemo(() => (<Son1 />) ), [] )} </div> </CustomizeProvider> ); }; export default Parent;Copy the code

βœ‹πŸ» Click to view the online demo

In this example, we still use the optimization Hook of useMemo to optimize the components.

πŸ€™ πŸ€™ πŸ€™ summary

In this article, three optimization methods are introduced, which are mainly used:

  • πŸ€™ useMemo
  • πŸ€™ memo
  • πŸ€™ children
  • πŸ€™ useCallback
  • πŸ€™ PureComponent
  • πŸ€™ Extract the status component
  • πŸ€™ extracts the invariant value

These optimization methods can be used in different situations, so if you use them, be sure to use appropriate optimization methods according to the situation of the code.

If you know of other optimization methods, you can also leave a message in the comments section!!

πŸ‘πŸ‘πŸ‘ past wonderful

  • πŸ‘ project to introduce ESBuild, compile directly to heaven!!
  • πŸ‘ Best practices for using WebComponents in React
  • πŸ‘ How can I optimize the 50+MB APP package to 4.2MB?
  • Can you explain the principle of πŸ‘ Styleds – Components?
  • πŸ‘ Interviewer: Can you talk about the difference between extends and a parasitic composite inheritance stereotype?
  • πŸ‘ Front-end Leader asked me to talk to my colleagues about event loops
  • πŸ‘React compares status updates with Vue
  • πŸ‘ How do I React to a tree shuttle box?
  • πŸ‘ 10 Mistakes to avoid when using React
  • πŸ‘ Webpack Packaging Optimization Direction Guide (Theory)
  • πŸ‘ Vue encountered drag and drop dynamically generated components?
  • πŸ‘JS Coding tips, most people can’t!!
  • πŸ‘TypeScript Doesn’t it smell? Come on!