Реагировать на хуки для обновления состояния на основе предыдущего значения состояния

Используя хуки для обновления состояния на основе предыдущего значения состояния, я не понимаю, почему изменение существующего объекта и передача его в setState() - это плохо. Я знаю, что это не вызовет повторного рендеринга, поскольку состояние по-прежнему указывает на ту же ссылку, но помимо этого, в чем проблема? Я не понимаю, как клонирование массива, его изменение и передача его в setState() устраняет неизвестную проблему.

const [bigArr, setBigArr] = setState(Array(SOME_BIG_NUMBER).fill(false));
// (1) This seems to work, but is bad for some reason.  But why?
bigArr[325] = true;
setBigArr(bigArr);
// (2) This is preferable for some reason. Why?
bigArrCopy = bigArr.slice();
bigArrCopy[325] = true;
setBigArr(bigArrCopy);
// (3) Is this OK?  Why/Why not?
setBigArr(bigArrCopy => {
    bigArrCopy[325] = true;
    return bigArrCopy;
});

3 ответа

Я знаю, что это не вызовет повторного рендеринга, поскольку состояние по-прежнему указывает на ту же ссылку, но помимо этого, в чем проблема?

Этого мало? Причина установки состояния заключается в том, что вы хотите, чтобы компонент повторно отрисовался. Если вы пытаетесь повторно выполнить рендеринг, но этого не происходит, это довольно серьезная ошибка.

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

Первый случай не будет работать должным образом, он не будет повторно отображать компонент, потому что React использует неглубокое сравнение, что означает, что он будет сравнивать местоположение объекта и массива, если местоположение не изменится, React не вызовет повторный рендеринг

// (1) This will not re-render the component, cuz the location of bigArr are not changed
bigArr[325] = true;
setBigArr(bigArr);

То же самое и с третьим случаем

Вы можете сказать, что можете исправить это по телефону setBigArr([...bigArr]);. Но есть еще проблемы

В простой ситуации вы получите неожиданный результат

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

export default function App() {
  const [arr, setArr] = useState([1]);

  const handleClick = () => {
    createTimeout()

    // Case1: directly set state 
    arr[0]= 2
    setArr(arr);

    // Case1: Not directly set state 
    // const newArr = [...arr]
    // newArr[0]= 2
    // setArr(newArr);
  };

  const createTimeout = () =>{
    setTimeout(() =>{
      // You expect number 1 here because you call this before update state
      console.log(arr[0])
    },2000)
  }
  return (
    <div className="App">
      <h1>{arr[0]}</h1>
      <div onClick={handleClick}>change</div>
    </div>
  );
}

Мы называем createTimeout перед setState так что мы будем ожидать числа 1будут зарегистрированы, но:Случай 1: вы получите номер 2, потому что вы изменили исходный массив
Случай 2: вы получите номер 1 (ожидается)

Проверьте коды и ящик

React проверяет, есть ли bigArrState!== prevBigArrState перед повторным рендерингом. Он не проверяет содержимое массива. Он проверяет, является ли это одним и тем же экземпляром. В вашем первом примере это приведет к false, и компонент не будет повторно визуализироваться. Когда вы используете bigArr.slice(), вы создаете целый новый массив, поэтому bigArrState!== prevBigArrState возвращает true, позволяя компоненту повторно отрисоваться.

Ваш последний пример вызовет проблемы, потому что func средства обновления не передается bigArrCopy, а скорее bigArrState (тот же экземпляр).

https://reactjs.org/docs/react-component.html

Вместо того, чтобы создавать и хранить в памяти весь клон, вы можете сделать следующее:

setBigArr([
  ...bigArr.slice(0, 325),
  true,
  ...bigArr.slice(326),
]);
Другие вопросы по тегам