This is the fourth day of my participation in the August More text Challenge. For details, see: August More Text Challenge

preface

React Hooks have been around for a long time. React itself and its ecology are groping for progress. Based on my experience with React, I’d like to share with you some ways to optimize the performance of React Hooks. As to React the use of Hooks. This article is not to do too much explanation, to view the react.docschina.org/docs/hooks-…

The key

The key to solving the performance problem is the handling of repeated renderings of components. After React Hooks were used, many people complained about the increase in render times. For example, we split different data into multiple state variables, and each value change triggers a render.

For example

Now we have a Parent component, and the child depends on the name property passed in by the Parent component, but changes to the name and text properties of the Parent component cause the Parent function to be executed again, so even if the child component is passed with the props property unchanged, and even if the child does not depend on any props property, Causes the child component to be rerendered

const Child = ((props: any) = > {
  console.log("Public number: front-end development hobbyist sub-component, I updated...");
  return (
      <div>
          <h3>Child components</h3>
          <div>text:{props.name}</div>
          <div>{new Date().getTime()}</div>
      </div>)})const Parent = () = > {
  const [count, setCount] = useState(0);
  const [text, setText] = useState("")
  const handleClick = () = > {
      setCount(count + 1);
  }
  const handleInputChange = (e) = > {
      setText(e.target.value)
  }
  return (<div>
      <input onChange={handleInputChange} />
      <button onClick={handleClick}>+ 1</button>
       <div>count:{count}</div>
      <Child name ={text}/>
  </div>)}Copy the code

If handleInputChange or handleClick is triggered, the subcomponent will output to the console. If handleInputChange is triggered, the subcomponent will output to the console. So even if the child component is passed props without any changes, and even if the child does not depend on any props properties, the child component will be rerendered.

To solve the problem of repeated rendering, use React’s hand-made upgrade son, who has three methods for optimization: React. Memo useCallback useMemo.

  • React. Memo: Do simple data type comparisons like class component PureComponent

  • UseMemo: Can be used to compare data of complex types, such as Object Array, etc

  • UseCallback: Upgrade version used to control whether the component needs to be updated when the transfer function is controlled

React.memo

When you wrap a child component with memo, the child component will only be rerendered if the props change. Using MEMO provides some performance improvements.


const Child = React.memo((props: any) = > {
    console.log("Public number: front-end development hobbyist sub-component, I updated..."); // The child component will be rerendered only if the props property changes and the set name property changes
    return (
        <div>
            <h3>Child components</h3>
            <div>text:{props.name}</div>
            <div>{new Date().getTime()}</div>
        </div>)})const Parent = () = > {
    const [count, setCount] = useState(0);
    const [text, setText] = useState("")
    const handleClick = () = > {
        setCount(count + 1);
    }
    const handleInputChange = (e) = > {
        setText(e.target.value)
    }
    return (<div>
        <input onChange={handleInputChange} />
        <button onClick={handleClick}>+ 1</button>
         <div>count:{count}</div>
        <Child name ={text}/>
    </div>)}Copy the code

But if the passed props contains functions, the parent component will create a new function each time it re-renders the function, so the child of the passed function will still be re-rendered, even though the content of the function remains the same. How to solve this problem, we want to cache functions as well, so we introduce useCallback

useCallback

The useCallback function is used to cache functions. If a function in the parent component is passed to the child component as props, a new instance of the function as props will be created as soon as the parent component changes its data and the function is executed again

You can cache functions using useCallback. Use with memo

const Child = React.memo((props: any) = > {
    console.log("Public number: front-end development hobbyist sub-component, I updated...");
   return (
       <div>
           <h3>Child components</h3>
           <div>text:{props.name}</div>
           <div> <input onChange={props.handleInputChange} /></div>
           <div>{new Date().getTime()}</div>
       </div>)})const Parent = () = > {
   const [count, setCount] = useState(0);
   const [text, setText] = useState("")
   const handleClick = () = > {
       setCount(count + 1);
   }
   const handleInputChange = useCallback((e) = > {
       setText(e.target.value )
   },[])
   return (<div>
       <button onClick={handleClick}>+ 1</button>
       <div>count:{count}</div>
       <Child name={text} handleInputChange={handleInputChange}/>
   </div>)}Copy the code

How can we use the second parameter dependency of useCallback


/ / modify handleInputChange
const handleInputChange =useCallback((e) = > {
        setText(e.target.value + count)
    },[])
Copy the code

What happens when the count changes in the example above?

Count changes, but handleInputChange does not depend on any item, so handleInputChange is only called once during initialization and is cached. The count inside the function is always 0 when the text changes or the count changes. As for why you need to see the useCallback source code after the answer.

So you need to add count to the dependency, regenerate the function after the count change, and change the count value inside the function

const handleInputChange =useCallback((e) = > {
        setText(e.target.value + count)
    },[count])
Copy the code

useMemo

GetTotal is supposed to be an expensive operation, but the result of this function depends only on count and price, but DOM rerendering will also cause this function to be executed due to color changes. UseMemo is used to cache the result of execution of this function and only recalculate when dependencies change

const Parent = () = > {
    const [count, setCount] = useState(0);
    const [color,setColor] = useState("");
    const [price,setPrice] = useState(10);
    const handleClick = () = > {
        setCount(count + 1);
    }
    const getTotal = () = >{
        console.log("GetTotal executes...") // This function depends on count and price, but a color change will also cause the function to be executed
        return count * price
    }
    return (<div>

        <div>Color:<input onChange={(e)= > setColor(e.target.value)}/></div>
        <div>The unit price:<input value={price}  onChange={(e)= > setPrice(Number(e.target.value))}/></div>
        <div>Quantity: {count}<button onClick={handleClick}>+ 1</button></div>
        <div>Total price: {getTotal ()}</div>
    </div>)}Copy the code

Note that useMemo caches the result of function execution

const Parent = () = > {
    console.log("Parent executes...")
    const [count, setCount] = useState(0);
    const [color,setColor] = useState("");
    const [price,setPrice] = useState(10);
    const handleClick = () = > {
        setCount(count + 1);
    }
    const getTotal = useMemo(() = >{
        console.log("GetTotal executes...")
        return count * price
    },[count, price])
    return (<div>

        <div>Color:<input onChange={(e)= > setColor(e.target.value)}/></div>
        <div>The unit price:<input value={price}  onChange={(e)= > setPrice(Number(e.target.value))}/></div>
        <div>Quantity: {count}<button onClick={handleClick}>+ 1</button></div>
        <div>The total price: {getTotal}</div>
    </div>)}Copy the code

At this point, the problem of repeated rendering is basically solved.

Functional programming is highly recommended in React, allowing data immutability as a means of optimization. In the React class era, I used immutable. Js and Redux to build business, which perfectly cooperated with PureComponnet in React and maintained very good performance. But when you combine iT with typescript in React hooks, it’s a little out of place, and the type support isn’t perfect. Here you can try immer.js, the introduction cost is small, writing a lot of concise.

Finally, I recommend a useful hooks library: ahooks: https://ahooks.js.org/zh-CN/hooks/async

Ahooks is a React Hooks library dedicated to providing commonly used and high quality Hooks. There are also hooks for performance optimization, such as: usePersistFn useCreation…

The last

If you think this article is helpful to you, I hope you can give me a thumbs-up support 💪

You can also follow the WX public number: front-end development fans reply to add group, together to learn front-end skills public number contains a lot of vue React actual combat selected resources tutorial, welcome to pay attention