[React Cooks Series] USEMEMO

JavaScript React.js

предисловие

Поскольку функциональные компоненты React проще в использовании (по сравнению с классовыми компонентами), я сосредоточусь на использовании функциональных компонентов для запуска разработки. В этой серии блогов я поделюсь тем, что узнал об API серии Hook. Серия крючков в основном делится на следующие:

Memo

В эпоху классов мы обычно используем pureComponent для поверхностного сравнения данных.После введения функции Hook мы можем использовать Memo для повышения производительности.

Перед этим проведем эксперимент

import React, { useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [n, setN] = useState(0);
  const [m, setM] = useState(10);
  console.log("执行最外层盒子了");
  return (
    <>
      <div>
        最外层盒子
        <Child1 value={n} />
        <Child2 value={m} />
        <button
          onClick={() => {
            setN(n + 1);
          }}
        >
          n+1
        </button>
        <button
          onClick={() => {
            setM(m + 1);
          }}
        >
          m+1
        </button>
      </div>
    </>
  );
}
function Child1(props) {
  console.log("执行子组件1了");
  return <div>子组件1上的n:{props.value}</div>;
}
function Child2(props) {
  console.log("执行子组件2了");
  return <div>子组件2上的m:{props.value}</div>;
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

В приведенном выше коде я настроил два подкомпонента, соответственно прочитал n и m в родительском компоненте, а затем установил две кнопки щелчка в родительском компоненте.При нажатии добавляю 1 к набору n и m соответственно. Вот результат лог-консоли при первом рендеринге

执行最外层盒子了 
执行子组件1了 
执行子组件2了 

Как и предполагалось, при рендеринге сначала введите функцию приложения, выполните, найдите две дочерние функции внутри, выполните, создайте виртуальный дом, создайте объектный дом и, наконец, отобразите экран на странице.

Оптимизируйте с помощью Memo

Когда я нажимаю кнопку n+1, в это время n в состоянии должно быть +1, что также снова запускает рендеринг и обновляет новое n в представлении.Посмотрим еще раз на консоль

执行最外层盒子了 
执行子组件1了 
执行子组件2了 
+ 执行最外层盒子了 
+ 执行子组件1了 
+ 执行子组件2了 //为什么组件2也渲染了,里面的m没有变化 

Вы обнаружите, что также отображается подкомпонент 2. Очевидно, что react повторно выполняет все функции снова и повторно выполняет подкомпонент 2, у которого нет данных n.

Как оптимизировать? мы можем использоватьmemoИзмените дочерний компонент на следующий код

const Child1 = React.memo((props) => {
  console.log("执行子组件1了");
  return <div>子组件1上的n:{props.value}</div>;
});

const Child2 = React.memo((props) => {
  console.log("执行子组件2了");
  return <div>子组件2上的m:{props.value}</div>;
});

Нажмите еще раз, чтобы попробовать?

执行最外层盒子了 
执行子组件1了 
执行子组件2了 
+ 执行最外层盒子了 
+ 执行子组件1了 

Вы обнаружите, что подкомпонент 2 не выполняется.

В этом случае react выполнит только компонент, соответствующий изменению состояния, а компонент, который не изменился, будет повторно использовать последнюю функцию.Может быть, memo также означает память, что означает запоминание последней функции, а не ее повторное выполнение (я предполагаю - -!!)

ошибка

Хотя приведенный выше код был оптимизирован для повышения производительности, в нем будет ошибка

Приведенный выше код контролируется родительским компонентом.<button>Да, а если я передам функцию, контролирующую состояние, дочернему компоненту?

 <Child2 value={m} onClick={addM} /> //addM是修改M的函数

Нажмите кнопку, чтобы разрешить n+1

执行最外层盒子了 
执行子组件1了 
执行子组件2了 
+ 执行最外层盒子了 
+ 执行子组件1了 
+ 执行子组件2了 

Подкомпонент 2 выполняется снова.

Почему это происходит? Поскольку приложение выполняется повторно, оно изменит адрес функции addM (функция представляет собой сложный тип данных), а addM будет передан подкомпоненту 2 в качестве реквизита, что вызовет повторное выполнение подпрограммы. -функция компонента 2.

useMemo

В настоящее время для решения проблемы следует использовать useMemo.

useMemo(()=>{},[])

useMemo получает два параметра, функцию и массив (фактически зависимость), возвращаемую функцию в функции и зависимость в массиве.

const addM = useMemo(() => {
    return () => {
      setM({ m: m.m + 1 });
    };
  }, [m]); //表示监控m变化

Способ его использования аналогичен useEffect.

useCallback

Код выше очень странный

useMemo(() => {
    return () => {
      setM({ m: m.m + 1 });
    };
  }, [m])

React подготовил для нас синтаксический сахар, используйте Callback. это написано так

  const addM = useCallback(() => {
    setM({ m: m.m + 1 });
  }, [m]);

Выглядит более нормально?

окончательный код

import React, { useCallback, useMemo, useState } from "react";
import ReactDOM from "react-dom";

import "./styles.css";

function App() {
  const [n, setN] = useState(0);
  const [m, setM] = useState({ m: 1 });
  console.log("执行最外层盒子了");
  const addN = useMemo(() => {
    return () => {
      setN(n + 1);
    };
  }, [n]);
  const addM = useCallback(() => {
    setM({ m: m.m + 1 });
  }, [m]);
  return (
    <>
      <div>
        最外层盒子
        <Child1 value={n} click={addN} />
        <Child2 value={m} click={addM} />
        <button onClick={addN}>n+1</button>
        <button onClick={addM}>m+1</button>
      </div>
    </>
  );
}
const Child1 = React.memo((props) => {
  console.log("执行子组件1了");
  return <div>子组件1上的n:{props.value}</div>;
});

const Child2 = React.memo((props) => {
  console.log("执行子组件2了");
  return <div>子组件2上的m:{props.value.m}</div>;
});

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Рекомендуется копировать прямо вcodesanboxПроверить

Суммировать

  • использоватьmemoможет помочь нам оптимизировать производительность, пустьreactНет необходимости выполнять ненужные функции
  • Так как адрес сложного типа данных может измениться, адрес передается дочернему компонентуpropsОн также изменится, поэтому лишние функции все равно будут выполняться, поэтому он используетсяuseMemoэтот API
  • useCallbackдаuseMemoсинтаксический сахар для