Почему реквизиты, передаваемые в memo, не сохраняют значение

Я пытаюсь оптимизировать рендеринг моего списка React, используя функцию React memo для ручного сравнения свойств. Я создал список простых "переключателей":

import React, { useState } from "react";
import "./styles.css";
import { Toggle } from "./Toggle";

export default function App() {
  const [list, setList] = useState({ a: true, b: true, c: true });
  const handleClick = x => {
    console.log(list);
    const currentValue = list[x];
    setList({ ...list, [x]: !currentValue });
  };

  return (
    <div className="App">
      <h1>Hello CodeSandbox</h1>
      {Object.keys(list).map(x => (
        <Toggle key={x} isChecked={list[x]} name={x} onChange={handleClick} />
      ))}
    </div>
  );
}

Это кнопка "переключения":

import React from "react";

const areEqual = (prevProps, nextProps) => {
  return prevProps.isChecked === nextProps.isChecked;
};

const ToggleComponent = ({ isChecked, name, onChange }) => {
  return (
    <>
      <h1>{isChecked ? "This is true" : "This is false"}</h1>
      <button onClick={() => onChange(name)}>{name}</button>
    </>
  );
};

export const Toggle = React.memo(ToggleComponent, areEqual);

Моя проблема в том, что объект списка на самом деле не сохраняет ожидаемое значение. Каждый раз, когда я нажимаю на кнопки, я получаю одну и ту же кнопку по умолчанию{ a: true, b: true, c: true } (это видно в console.log из handleClick), но если я удалю areEqual функция, все снова работает правильно, и объект списка обновляется должным образом.

Пример песочницы кода

Отредактируйте cool-sun-wq1f6

РЕДАКТИРОВАТЬ:

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

Пример песочницы кода с массивом

Отредактируйте cool-sun-wq1f6

1 ответ

Решение

Это потому, что handleClick функция создается и передается один раз в ToggleСоставная часть. А такжеhandleClick's закрытие содержит старый list значение, поэтому при изменении старого значения оно не обновляется.

Самое простое решение - воспользоваться второй сигнатурой средства обновления состояния: функцией, которая принимает в качестве параметра старое значение состояния.

Поэтому всякий раз, когда он вызывается, response передаст ему старое значение состояния.

const handleClick = x => {
  setList(old => ({ ...old, [x]: !old[x] }));
};

Вам также необходимо memoize функция handleClick, потому что it is recreated at each render компонента, содержащего состояние:

const handleClick = React.useCallback(x => {
  setList(old => ({ ...old, [x]: !old[x] }));
}, [setList]);

Вот рабочие коды и ящик

Другие вопросы по тегам