react Hooks

React hooks were introduced in version 16.8. They were designed to write fully functional components using only function components, and can actually be considered enhancements to function components. **Hooks are new in React 16.8. They let you use state and other React functionality without having to write classes.

The memo,useCallback, and useMemo mentioned in this article are the hooks related to performance optimization in functional components

memo

React.memo(Component, myFunc) takes two arguments, a custom function and a comparison function. This method is similar to shouldComponentUpdate and pureComponent of the React Class component, where the second parameter is used to determine whether the component needs to be rerendered. In the example below, when the parent component’s props state changes, the child component also triggers an update. In fact, the child component does not receive any props, but renders a copy that does not change, so there is no need to re-render the child component.

const ChildComponent = () = > {
  console.log('Child component executed');
  return (
    <p>I am the content of the child component</p>)};const ParentComponent = () = > {
  // console.log(' parent component executed ');
  const [count, setCount] = useState<Number> (1);

  const changeCount = () = > {
    setCount(count + 1);
  };

  return (
    <>
      <button onClick={changeCount}>Click on the</button>
      <p>count is: </p>
      <p>{count}</p>
      <ChildComponent />
    </>)};Copy the code

In the last demo, we introduced react.Memo. Let’s take a look at the console print

const ChildComponent = memo(() = > {
  console.log('Child component executed');
  return (
    <p>I am the content of the child component</p>)});const ParentComponent = () = > {
  console.log('Parent component executed');
  const [count, setCount] = useState<Number> (1);

  const changeCount = () = > {
    setCount(count + 1);
  };

  return (
    <>
      <button onClick={changeCount}>Click on the</button>
      <p>count is: </p>
      <p>{count}</p>
      <ChildComponent />
    </>)};Copy the code

At this point, the child component is only rendered once when the component is mounted, and no update of the parent component will trigger a re-rendering of the child component.

In addition, just we said in the memo is to receive the second parameter, and above for custom comparison functions, when we pass in the custom function, child components whether or not to render depends on the value returned from this function, the function will be more old and new props are consistent, if consistent returns true, the child components will not render again right now, If false is returned, the child component is rerendered.

const myEquareFunc = (prevProps, nextProps) = > {
  console.log('prevProps is:', prevProps);
  console.log('nextProps is: ', nextProps);
  // Compare whether the new and old props are equal, true if they are equal, false otherwise
};
Copy the code

useCallBack

If we pass the props to the subcomponent, then the memo may not work and the subcomponent will still execute. The reason is that anonymous functions have different references after each rendering, resulting in re-rendering of child components.

const ChildComponent = memo(({ text, changeText }) = > {
  console.log('Child component executed');
  return (
    <>
      <p>text is: {text}</p>
      <button onClick={()= >ChangeText (' changeText ')}> button</button>
    </>)});const ParentComponent = () = > {
  const [number, setNumber] = useState<number>(1);
  const [text, setText] = useState<string>('I'm the copy that the parent passes to the child.');

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

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

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

As you can see from the demo above, although the memo is used by the child component, the child component is re-rendered each time in the parent component setNumber. In this case, useCallback can be used to optimize this behavior.

const ChildComponent = memo(({ text, changeText }) = > {
  console.log('Child component executed');
  return (
    <>
      <p>text is: {text}</p>
      <button onClick={()= >ChangeText (' changeText ')}> button</button>
    </>)});const ParentComponent = () = > {
  const [number, setNumber] = useState<number>(1);
  const [text, setText] = useState<string>('I'm the copy that the parent passes to the child.');

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

  const changeText = useCallback((newText) = >{ setText(newText); } []);// This dependency is essential, otherwise it will be executed on every render and useCallback will be meaningless

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

As you can see, the child component will only be rendered once when it initializes and clicks on the modify text. The rest of the time, no matter how much state the parent component updates, the child component will not be re-rendered.

useMemo

UseMemo also takes two parameters, officially described: passing a “create” function and a dependency array. UseMemo recalculates memory values only when one of the dependencies changes. This optimization helps avoid expensive calculations every time you render. UseCallback (fn, deps) is equivalent to useMemo(() => FN, deps). Directly on the demo

const ChildComponent = memo(({ infos, changeText }) = > {
  console.log('Child component executed');
  return (
    <>
      <p>text is: {infos.text}</p>
      <button onClick={()= >ChangeText (' changeText ')}> button</button>
    </>
  );
});

const ParentComponent = () = > {
  const [number, setNumber] = useState<number>(1);
  const [text, setText] = useState<string>('I'm the copy that the parent passes to the child.');

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

  const changeText = useCallback((newText) = >{ setText(newText); } []);// This dependency is essential, otherwise it will be executed on every render and useCallback will be meaningless

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

As you can see from the code above, if the state in the parent component is updated, the child component will still render. This is because a new object containing text is generated each time, and when the props changes, the child component is naturally rerendered.

const ChildComponent = memo(({ infos, changeText }) = > {
  console.log('Child component executed');
  return (
    <>
      <p>text is: {infos.text}</p>
      <button onClick={()= >ChangeText (' changeText ')}> button</button>
    </>
  );
});

const ParentComponent = () = > {
  const [number, setNumber] = useState<number>(1);
  const [text, setText] = useState<string>('I'm the copy that the parent passes to the child.');

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

  const changeText = useCallback((newText) = >{ setText(newText); } []);// This dependency is essential, otherwise it will be executed on every render and useCallback will be meaningless

  const result = useMemo(() = >({
    text,
  }), [text]);

  return (
    <>
      <button onClick={handleChange}>clike me</button>
      <p>count: {number}</p>
      <ChildComponent infos={result} changeText={changeText}  />
    </>)};export default ParentComponent;
Copy the code

When we use useMemo to wrap the object passed in, we find that the child components are no longer re-rendered, which is a good way to do unnecessary rendering.

React useMemo can also be used to reduce the amount of computation required by useMemo.

const [count, setCount] = useState<number>(0);

  const expensiveFn = () = > {
    console.log('Method executed');
    let result = 0;
    for(let i = 0; i < 10000; i++) {
        result += i;
    }
    return result;
  };

  const base = expensiveFn();

  return (
    <>
      <h1>count: {count} </h1>
      <button onClick={()= >setCount(count + base)}> click me </button>
    </>
  );

Copy the code

Each time the demo button is clicked, the component is re-rendered and the method is re-rendered. But actually expensiceFn is a relatively expensive function, and the return value of the function is constant, which causes unnecessary waste of performance. UseMemo can then be used to cache the calculated values.

const MyComponent = () = > {
  const [count, setCount] = useState<number>(0);

  const expensiveFn = () = > {
    console.log('Method executed');
    let result = 0;
    for(let i = 0; i < 10000; i++) {
        result += i;
    }
    return result;
  };

  const base = useMemo(expensiveFn, []);

  return (
    <>
      <h1>count: {count} </h1>
      <button onClick={()= >setCount(count + base)}> click me </button>
    </>)};Copy the code

So expensiveFn’s value is only calculated once at the beginning. The first argument to useMemo is a function whose return value is cached and used as useMemo’s return value. The second argument is an array dependency. If the value in the array changes, the function in the first argument is executed again. The value returned by the function is cached as the return value of useMemo. If no dependent array is provided, useMemo evaluates the new value every time it renders.

Reference article: reactjs.org/docs/hooks-… Juejin. Cn/post / 684490…