Реагирует на неожиданное поведение массива внутри useEffect с помощью setInterval

#reactjs #react-hooks

Вопрос:

Значение с именем «a» в нижнем коде ведет себя не ожидаемо на локальном, а не на статическом сайте.

Я ожидаю, что a увеличивается на 100 в секунду, но увеличивается на 200 в секунду.

Эта проблема возникает при запуске приложения с помощью следующих команд.

 next dev
react-scripts start
 

Как я могу решить эту проблему? (И является ли это ожидаемым поведением крючков react?) Если возможно, не используйте useReducer.

Я уже прочитал этот сайт.

 import { memo, useEffect, useState, VFC } from "react";

const StateLearning: VFC = () => {
  const [a, setA] = useState([
    [1, 2],
    [0, 4],
  ]);


  // This moved expectedly
  // useEffect(() => {
  //   const eff = setInterval(() => {
  //     a[1][0]  = 100;
  //     console.log('add 100')
  //     setA([...a]);
  //   }, 1000);
  //   return () => clearInterval(eff);
  // }, [a]);

  // a[1][0] moves 0→100→300→500→……
  useEffect(() => {
    const eff = setInterval(() => {
      setA((prev) => {
        prev[1][0]  = 100;
        console.log("add 100");
        console.info(prev)
        return [...prev];
      });
    }, 1000);
    return () => clearInterval(eff);
  }, []);

  return (
    <div style={{ margin: "10%" }}>
      <div>
        <div>
          {a[0][0]}, {a[0][1]}
        </div>
        <div>
          {a[1][0]}, {a[1][1]}
        </div>
      </div>
    </div>
  );
};
 

P.S.

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

 …
// called by like onclick
const multiple = () => {
    setA((p) => {
      const newArray = [
        [
          p[0][0] * p[0][0]   p[0][1] * p[1][0],
          p[0][0] * p[0][1]   p[0][1] * p[1][1],
        ],
        [
          p[1][0] * p[0][0]   p[1][1] * p[1][0],
          p[1][0] * p[0][1]   p[1][1] * p[1][1],
        ],
      ];
      
      return [...newArray];
    });
  };
…
 

Комментарии:

1. Не уверен, в чем проблема с двойным обновлением, но если вы хотите иметь истинное обновление неизменяемого состояния, вам нужно это сделать prev => prev.map((row, i) => i === 1 ? row.map((col, j) => j === 0 ? (col 100) : col) : row) . В противном случае вы мутируете вложенный массив без создания копии codesandbox.io/s/agitated-wilson-fd89d?file=/src/App.js

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

Ответ №1:

Я создал эту песочницу, https://codesandbox.io/s/strange-mahavira-co66j

Мне кажется, это работает

   useEffect(() => {
    const eff = setInterval(() => {
      a[1][0]  = 100
      setA([...a])
    }, 1000)
    return () => {
      clearInterval(eff)
    }
  }, [setA])
 

Вам не нужно обязательно использовать prev , потому a что это объект в вашем случае. Обычно проблема возникает с примитивным значением.

Комментарии:

1. OP необходимо использовать форму версии обновления setState . Наличие объекта в качестве значения не меняет того, почему нам нужно его использовать. В настоящее время это работает только потому, что вы изменяете значение вложенного массива без создания копии с исходным значением, записанным в области обратного вызова. Если у кого-то есть другой способ редактирования значений, возникнет целая куча проблем. codesandbox.io/s/heuristic-tdd-77xm2?file=/src/App.js

2. это, кажется, работает, но я также хочу изменить значение » a » с помощью другой функции, такой как onClick(матрица, кратная). Когда функция setinterval перемещается, эта функция не применяла изменение функции onclick.