Front knowledge

Understand basic data types and reference data types

'a' === 'a'         // true

{} === {}                 // false
Copy the code

API

  • React.memo()
  • React.useCallback()
  • React.useMemo()

React.memo()

Look at the problem

In React, when the props or state of a component changes, it will re-render, resulting in unnecessary rendering scenes in actual development. For example: Parent component:

import { useState } from "react";
import { Child } from "./child";

export const Parent = () = > {
  const [count, setCount] = useState(0);
  const increment = () = > setCount(count + 1);

  return (
    <div>
      <button onClick={increment}>Number of clicks: {count}</button>
      <Child />
    </div>
  );
};

Copy the code

Child components:

export const Child = ({}) = > {
  console.log("Rendered.");
  return <div>Child components</div>;
};
Copy the code

Clicking on the button in the parent component changes the value of the count variable, which causes the parent component to re-render with no changes to the child component (props, state), but a printout of the rendered child component is still visible in the console.

The props and state of the subcomponent are unchanged, and we do not want it to reproduce the rendering.

How to solve it? (the React. Memo ())

React. Memo () is a new property introduced in React V16.6 that controls the re-rendering of function components. React.memo() takes the component as an argument to the function memo. The return Child of the function is a new component. Child components:

import { memo } from "react";

export const Child = memo(() = > {
  console.log("Rendered.");
  return <div>Child components</div>;
});

Copy the code

Take a look at the effect:

Obviously, the child components are not re-rendered.

React.useCallback()

Look at the problem

In the example above, the parent component simply calls the child component without passing any properties to the child component. Parent component:

import { useState } from "react";
import { Child } from "./child";

export const Parent = () = > {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("Xiao Ming");
  const increment = () = > setCount(count + 1);

  const onClick = (name: string) = > {
    setName(name);
  };

  return (
    <div>
      <button onClick={increment}>Number of clicks: {count}</button>
      <Child name={name} onClick={onClick} />
    </div>
  );
};

Copy the code

Child components:

import { memo } from "react";

export const Child = memo(
  (props: { name: string; onClick: (value: any) => void }) = > {
    const { name, onClick } = props;
    console.log("Rendered.", name, onClick);
    return (
      <>
        <div>Child components</div>
        <button onClick={()= >OnClick (" red ")}> change the name value</button>
      </>); });Copy the code

Click on the parent component Count and see that the child component is rerendered each time.

Analyze the reasons:

  • Clicking the parent component button changes the count variable in the parent component, which causes the parent component to re-render;
  • When the parent component rerenders, the onClick function is recreated, that is, the onClick property passed to the child component changes, causing the child component to render;
  • If the props passed to the child component is onlyBasic data typesWill not be re-rendered.

Note: If you pass the setName destructed by useState directly to the child component, the child component will not repeat the rendering because the destructed is an Memoized function.

import { useState } from "react";
import { Child } from "./child";

export const Parent = () = > {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("Xiao Ming");
  const increment = () = > setCount(count + 1);

  return (
    <div>
      <button onClick={increment}>Number of clicks: {count}</button>
      <Child name={name} setName={setName} />
    </div>
  );
};

Copy the code

How to solve it? (the React. UseCallback ())

Passing the inline callback function and the array of dependencies as arguments to useCallback returns the memoized callback of the callback function, which is updated only when a dependency changes. This is useful when you pass callback data to child components that are optimized and use reference equality to avoid unnecessary rendering (such as shouldComponentUpdate). \

  • memoizedCallback function: The first time a function is called with a set of arguments, the parameters and computed results are cached, and the next time the function is called with the same arguments, the corresponding cached results are returned. (Return drinkable, so identical to ===)

Note: The dependency array is not passed as an argument to the callback function. Conceptually, though, it looks like this: all values referenced in callback functions should appear in dependency arrays.

Modify the parent component’s onClick method by wrapping a layer with the useCallback hook function.

import { useCallback, useState } from "react";
import { Child } from "./child";

export const Parent = () = > {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("Xiao Ming");
  const increment = () = > setCount(count + 1);

  const onClick = useCallback((name: string) = >{ setName(name); } []);return (
    <div>
      <button onClick={increment}>Number of clicks: {count}</button>
      <Child name={name} onClick={onClick} />
    </div>
  );
};

Copy the code

Click the parent component count and the child component will not be rerendered.

React.useMemo()

Look at the problem

In the example above, the name property is a string. What if it were passed instead? The parent component:

import { useCallback, useState } from "react";
import { Child } from "./child";

export const Parent = () = > {
  const [count, setCount] = useState(0);
  // const [userInfo, setUserInfo] = useState({name: "small ", age: 18});
  const increment = () = > setCount(count + 1);
  const userInfo = { name: "Xiao Ming".age: 18 };

  return (
    <div>
      <button onClick={increment}>Number of clicks: {count}</button>
      <Child userInfo={userInfo} />
    </div>
  );
};

Copy the code

Child components:

import { memo } from "react";

export const Child = memo(
  (props: { userInfo: { name: string; age: number } }) = > {
    const { userInfo } = props;
    console.log("Rendered.", userInfo);
    return (
      <>
        <div>Name: {the userInfo. Name}</div>
        <div>Age: {the userInfo. Age}</div>
      </>); });Copy the code

Click on the parent component Count and see that the child component is rerendered each time. Analyzing the cause is the same as calling a function:

  • Click the parent component button to trigger the parent component to re-render;
  • Parent component rendering,Const userInfo = {name: "xiao Ming ", age: 18};One line regenerates a new object, causing the value of the userInfo property passed to the child component to change, which in turn causes the child component to rerender.
  • Note: If useduseStateDestruct userInfo, the subcomponents will not be rendered repeatedly because the destruct is an Memoized value.

How to solve it? (the React. UseMemo ())

Use useMemo to wrap object properties in a layer.

import { useMemo, useState } from "react";
import { Child } from "./child";

export const Parent = () = > {
  const [count, setCount] = useState(0);
  // const [userInfo, setUserInfo] = useState({name: "small ", age: 18});
  const increment = () = > setCount(count + 1);
  const userInfo = useMemo(() = > ({ name: "Xiao Ming".age: 18 }), []);

  return (
    <div>
      <button onClick={increment}>Number of clicks: {count}</button>
      <Child userInfo={userInfo} />
    </div>
  );
};

Copy the code

UseMemo () returns a memoized value.

You pass in the create function and the dependency array as arguments to useMemo, which recalculates memoized values only when a dependency changes. This optimization helps avoid costly calculations every time you render.

Remember that functions passed into useMemo are executed during rendering. Please do not perform non-rendering operations inside this function. Operations such as side effects are used by useEffect, not useMemo.

If the dependency array is not provided, useMemo evaluates the new value each time it renders.