useEffect() 钩子在状态改变时不会更新

时间:2021-05-29 18:08:21

标签: reactjs react-hooks

我有以下代码:

import React, { useState, useEffect} from 'react';
import Board from './Board';
import Player from '../functions/Player'

function Game() {

  const [computer, setComputer] = useState(Player(1,false));
  const [computerBoard, setComputerBoard] = useState(new Array(100).fill(-1));

  const sendAttack = (location) => {
    // Activates everytime I click Board component
    let compCopy = computer;
    compCopy.receiveAttack(location);
    setComputer(compCopy);
    setComputerBoard(compCopy.getHitBoard());
    console.log(compCopy.getHitBoard()); // always prints, each time I do sendAttack()
    // and the hitBoard does change, shows a different hitBoard everytime I click!
  }

  useEffect(() => {
    console.log('something has changed') // print (twice for some reason) only on render, why?
    
  })

  useEffect(() => {
    console.log('computer hit has changed') // prints only on render and on the first setAttack()
  },[computer]);

  useEffect(() => {
    console.log('computer board has changed') // prints on render
    // and once it works but only on first sendAttack()
  },[computerBoard])

  return (
    <div className="game">
      <Board hitBoard ={computer.getHitBoard()} sendAttack={sendAttack} />
    </div>
  );
}

export default Game;

问题是我希望 Board 组件 hitBoard prop 会在 sendAttack() 完成更新时不断更新。但是除了渲染之外它永远不会更新它(实际上只有第一次 sendAttack() 一次)。我不明白为什么当我 setComputer 它不启动 useEffect()

我的代码中是否有明显的错误?感谢您的提示。

2 个答案:

答案 0 :(得分:2)

我不认为您的 compCopy 实际上是一个新对象。如果对象没有不同,那么 setComputer 可能不会不同,因此不会触发效果。尝试类似 let compCopy = makeANewCopyOf(computer) 的内容,这样 compCopy 是您自己克隆的全新对象。

答案 1 :(得分:2)

上面@Atmas 给出的答案是正确的。我会通过让 receiveAttack() 返回一个新对象来稍微调整它,该对象是带有其他更改的传入对象的副本。一般来说,这是一个更强大的数据管理解决方案 - 不要修改对象,创建新对象并返回它们。此外,在 React 和各种 React 库中,你会经常遇到比较对象 ID 而不是对象内容的想法。

这是一个有用的指南,它准确地讨论了您要做什么 - 使用对象作为 React 状态变量:

https://blog.logrocket.com/a-guide-to-usestate-in-react-ecb9952e406c/#usinganobjectasastatevariablewithusestatehook

此外,您不需要 useEffect() 来调试这些东西。当组件重新渲染时,将在 return 语句正上方放置一个常规的 ol' 控制台日志。