Состояние обновления в использованииEffect показывает предупреждение - PullRequest
0 голосов
/ 10 апреля 2020

Я хочу обновить состояние после обновления какого-либо другого состояния:

export default function App() {
  const [diceNumber, setDiceNumber] = useState(0);
  const [rolledValues, setRolledValues] = useState([
    { id: 1, total: 0 },
    { id: 2, total: 0 },
    { id: 3, total: 0 },
    { id: 4, total: 0 },
    { id: 5, total: 0 },
    { id: 6, total: 0 }
  ]);

  const rollDice = async () => {
     await startRolingSequence();
  };

const startRolingSequence = () => {
  return new Promise(resolve => {
    for (let i = 0; i < 2500; i++) {
      setTimeout(() => {
        const num = Math.ceil(Math.random() * 6);
        setDiceNumber(num);
      }, (i *= 1.1));
  }

    setTimeout(resolve, 2600);
});
};

  useEffect(()=>{
    if(!diceNumber) return;
    const valueIdx = rolledValues.findIndex(val => val.id === diceNumber);
    const newValue = rolledValues[valueIdx];
    const {total} = newValue;
    newValue.total = total + 1;

    setRolledValues([
      ...rolledValues.slice(0,valueIdx),
      newValue,
      ...rolledValues.slice(valueIdx+1)
    ])

  }, [diceNumber]);



  return (
    <div className="App">
      <button onClick={rollDice}>Roll the dice</button>
      <div> Dice Number: {diceNumber ? diceNumber : ''}</div>
    </div>
  );
}

Вот песочница

Когда пользователь бросает кости, пара setTimeouts изменит значение состояния и разрешит в конце концов. Как только он разрешится, я хочу отслеживать результат в массиве объектов.

Так что, когда я пишу это так, это работает, но eslint дает мне предупреждение о пропавшей зависимости. Но когда я добавлю зависимость, useEffect закончится навсегда l oop.

Как добиться обновления состояния после обновления состояния, не вызывая вечного l oop?

Ответы [ 2 ]

2 голосов
/ 10 апреля 2020

Вот способ настроить его, который сохраняет эффект использования и не имеет проблемы с зависимостью в этом эффекте: https://codesandbox.io/s/priceless-keldysh-wf8cp?file= / src / App. js

Это изменение в логи c вызова setRolledValues ​​включает в себя избавление от случайной мутации в массиве rollValues, которая потенциально может вызвать проблемы, если вы используете его в других местах, так как все состояния React должны работать с неизменной силой, чтобы предотвратить проблемы .

setRolledValues ​​был изменен, чтобы использовать опцию обратного вызова состояния для предотвращения требования зависимости.

 useEffect(() => {
    if (!diceNumber) return;

    setRolledValues(rolledValues => {
      const valueIdx = rolledValues.findIndex(val => val.id === diceNumber);
      const value = rolledValues[valueIdx];
      const { total } = value;
      return [
        ...rolledValues.slice(0, valueIdx),
        { ...value, total: total + 1 },
        ...rolledValues.slice(valueIdx + 1)
      ];
    });
  }, [diceNumber]); 

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

Вместо этого вы можете переместить логи c в обратный вызов rollDice, что избавит от обеих проблем, которые были происходит. https://codesandbox.io/s/stoic-visvesvaraya-402o1?file= / src / App. js

Я добавил useCallback вокруг rollDice, чтобы он не менял ссылки, поэтому его можно использовать в useEffects.

  const rollDice = useCallback(() => {
    const num = Math.ceil(Math.random() * 6);
    setDiceNumber(num);
    setRolledValues(rolledValues => {
      const valueIdx = rolledValues.findIndex(val => val.id === num);
      const value = rolledValues[valueIdx];
      const { total } = value;
      return [
        ...rolledValues.slice(0, valueIdx),
        // newValue,
        { ...value, total: total + 1 },
        ...rolledValues.slice(valueIdx + 1)
      ];
    });
  }, []);
1 голос
/ 10 апреля 2020

Вы должны иметь возможность просто вставить все логи c в setRolledValues

useEffect(() => {
  if (!diceNumber) return;

  setRolledValues((prev) => {
    const valueIdx = prev.findIndex(val => val.id === diceNumber);
    const newValue = prev[valueIdx];
    const { total } = newValue;
    newValue.total = total + 1;
    return [
      ...prev.slice(0, valueIdx),
      newValue,
      ...prev.slice(valueIdx + 1)
    ]
  })

}, [diceNumber]);

РЕДАКТИРОВАТЬ: Как уже указывали другие, useEffect для этого приложения, кажется, не подходит, вместо этого вы можете просто обновить setRolledValues в своей функции.

Если есть какая-то базовая система, нам не показывают, где вы должны использовать шаблон наблюдателя, подобный этому, вы вместо этого можно изменить тип данных diceNumber на объект, поэтому последующие вызовы на тот же номер вызовут useEffect

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...