Как решить проблему с закрытием при использовании функционального компонента React с React.memo?

Я использую функциональные компоненты React вместе с React.memo, но у меня возникла проблема, которая, на мой взгляд, связана с закрытием JavaScript. (Я также использую Immer для неизменности, но считаю, что это не влияет на ситуацию.)

Ниже представлена ​​упрощенная версия ситуации:

import React, { useState } from 'react';
import produce from "immer";

const initialData = [
  {id: 1, value: 0},
  {id: 2, value: 0},
  {id: 3, value: 0}
]

const ChildComponent = memo(
  ({ value, onChange }) => (
    <p>
      <input value={value} onChange={onChange} type="number" />
    </p>
  ),
  (prevProps, nextProps) => prevProps.value === nextProps.value
);

const ParentComponent = props => {
  const [data, setData] = useState(initialData);

  const onDataChange = id => event => {
    setData(
      produce(data, draft => {
        const record = draft.find(entry => entry.id === id);
        record.value = event.target.value;
      })
    );
  };

  return data.map(record => (
    <ChildComponent
      key={record.id}
      value={record.value}
      onChange={onDataChange(record.id)}
    />
  ));
};

Я использую React.memo, чтобы избежать ненужных повторных отображений ChildComponents, значение которых не изменилось, но таким образом ChildComponent хранит старую версию функции onChange, которая (в моем понимании из-за закрытий JavaScript) ссылается на старую версию данные.

В результате, когда я сначала изменяю значение первого ChildComponent, а затем значение другого ChildComponent, значение первого ChildComponent восстанавливается до начального значения.

Воспроизведение ситуации можно найти в этой песочнице.

После поиска в Интернете я нашел решение этой проблемы - использовать ссылку внутри функции onChange, чтобы получить актуальные данные, например:

const ParentComponent = props => {
  const [data, setData] = useState(initialData);
  const dataRef = useRef();
  dataRef.current = data;

  const onDataChange = id => event => {
    setData(
      produce(dataRef.current, draft => {
        const record = draft.find(entry => entry.id === id);
        record.value = event.target.value;
      })
    );
  };

  return data.map(record => (
    <ChildComponent
      key={record.id}
      value={record.value}
      onChange={onDataChange(record.id)}
    />
  ));
};

Песочницу с этим решением можно найти здесь.

Это решает проблему. Но я хотел спросить: это правильное решение? Или я неправильно использую React и React Hooks?


person Klajdi Çaushi    schedule 22.11.2019    source источник


Ответы (1)


Хотя использование ссылки является допустимым решением, вам следует использовать функциональный _1 _.

Если новое состояние вычисляется с использованием предыдущего состояния, вы можете передать функцию в setState. Функция получит предыдущее значение и вернет обновленное значение.

const onDataChange = id => event => {
  event.persist();
  setData(data =>
    produce(data, draft => {
      const record = draft.find(entry => entry.id === id);
      record.value = event.target.value;
    })
  );
};

 Редактировать sparkling-haze-e9y22

person Dennis Vash    schedule 22.11.2019
comment
Вы правы, я забыл про функционал setState. Спасибо - person Klajdi Çaushi; 22.11.2019