【问题标题】:Rect hooks props and state communication problemReact hooks props 和状态通信问题
【发布时间】:2019-03-23 13:32:19
【问题描述】:

我正在尝试基于 React Hooks 构建我的第一个 React 应用程序,并且很难弄清楚我的状态/道具和重新渲染发生了什么。我正在做一些小猜谜游戏。

我有一个父组件 GameContent,它正在向它的子组件(Round)传递一些道具:

  • currentRound(数字)
  • roundData(对象)
  • shiftRounds(函数)

通过“shiftRounds”我告诉我的父组件:“这个玩家给出了正确的答案”,然后函数会改变回合和得分(都处于父状态)。

虽然看起来很简单,但我在子组件中的函数检查答案是否正确,总是从第一轮获取数据。

我尝试了 React.useRef,尝试了不同的状态配置,但仍然找不到解决方案。

<GameContent />

const GameContent = ({ gameOptions, gameFinish }) => {

  const [gameData, setGameData] = useState([]);
  const [round, setRound] = useState(1);
  const [score, setScore] = useState([0, 0]);

  const [ligue, dateFrom, dateTo, rounds, player1, player2] = gameOptions;

  useEffect(() => {
    //
    // Function fetches football matches. 
    //
    (async () => {
      const matchesData = await fetchMatches(ligue, dateFrom, dateTo)
      setGameData(matchesData);
    })();
  }, [])

  const nextRound = (scoringPlayer) => {
    if (round <= rounds) {
      setScore(score => {
        score[scoringPlayer] += 1;
        return score
      });
      setRound(round => round + 1);
    } else {
      finishGame()
    }
  }

  return (
    <div className="text-center">
      <h2>Round {round}</h2>
      <h3>Score</h3>
      <h2>{`${score[0]} : ${score[1]}`}</h2>
      {(gameData.length) ? <Round currentRound={round} roundData={gameData[round - 1]} shiftRounds={nextRound} players={[player1, player2]} /> : <h1>Loading...</h1>}
    </div>
  )
}

<Round />

const Round = (props) => {
  const [isCheckingAnswer, setIsCheckingAnswer] = useState(false);

  useEffect(() => {
    //
    // Set eventListeners for keydown (too capture player's answer)
    //
    document.addEventListener('keydown', (e) => { handleKeyPress(e) });
    setIsCheckingAnswer(false);
    return () => {
      document.removeEventListener('keydown', (e) => { handleKeyPress(e) });
    }
  }, [props.roundData])

  const handleKeyPress = (e) => {
    if (!isCheckingAnswer) {
      e.preventDefault();
      setIsCheckingAnswer(true);
      if (keysMap[e.key] === checkGoodAnswer(props.roundData.homeTeamScore, props.roundData.awayTeamScore)) {
        const winingPlayer = isNaN(e.key / 2) ? 0 : 1;
        props.shiftRounds(winingPlayer);
      } else {
        setIsCheckingAnswer(false);
      }
    } else {
      return
    }
  }
  return (
    <>
      <Row>
        <Col xs={4}>
          <h2>{props.roundData.homeTeam}</h2>
        </Col>
        <Col xs={4}>
          <h2>vs</h2>
        </Col>
        <Col xs={4}>
          <h2>{props.roundData.awayTeam}</h2>
        </Col>
      </Row>
      <Row className="justify-content-center my-5">
        <Col xs={4}>
          <h3>{props.players[0]}</h3>
        </Col>
        <Col xs={4}>
          <h3>{props.players[1]}</h3>
        </Col>
      </Row>
    </>
  )
}

辅助函数

const checkGoodAnswer = (homeTeamScore, awayTeamScore) => {
  if (homeTeamScore > awayTeamScore) {
    return 'homeTeam'
  } else if (homeTeamScore < awayTeamScore) {
    return 'awayTeam'
  } else {
    return 'draw'
  }
}

const keysMap = {
  'a': 'homeTeam',
  'w': 'draw',
  'd': 'awayTeam',
  '4': 'homeTeam',
  '8': 'draw',
  '6': 'awayTeam',
}

获取的数据样本:

[
{awayTeam: "Paris Saint-Germain FC"
awayTeamScore: 2
homeTeam: "Manchester United FC"
homeTeamScore: 0},
{
awayTeam: "FC Porto"
awayTeamScore: 1
homeTeam: "AS Roma"
homeTeamScore: 2
},
...
]

【问题讨论】:

    标签: javascript reactjs react-hooks


    【解决方案1】:

    您应该添加 ALL 在您的useEffect 中使用的相关变量/函数作为依赖项。

    来自docs

    ...确保数组包含组件范围内的所有值(例如 props 和 state),这些值随时间变化并被效果器使用

    例如,在您的第一个效果中,您的数组中没有依赖项:

    useEffect(() => {
        //
        // Function fetches football matches. 
        //
        (async () => {
          const matchesData = await fetchMatches(ligue, dateFrom, dateTo)
          setGameData(matchesData);
        })();
      }, [])
    

    但你确实使用liguedateFromdateTo 等...

    react 团队提供了一个不错的 eslint 插件 (eslint-plugin-react-hooks) 可以帮助您解决此类问题,我建议您尝试一下。

    【讨论】:

    • 谢谢你的帮助,不过你引用的 useEffect 是故意与空数组一起使用的,所以 react 只会触发一次。我仍然无法解决这个问题,也许 linter 会帮助我。你有关于如何使用 eslint react hooks 插件和 create-react-app 的有用资源吗?谢谢
    • 您应该始终包含依赖项,当您不想要依赖项时,很少有罕见的边缘情况。至于对 create-react-app 的 eslint 插件支持,他们将其包含在最新版本中,您可以阅读它here
    【解决方案2】:

    我找到了解决方案。问题在于 addEventListener 中调用的函数范围, 我这样称呼它:

    document.addEventListener('keydown', (e) => { handleKeyPress(e) });
    

    但正确的称呼方式是:

    document.addEventListener('keydown', handleKeyPress);
    

    Sagiv 还指出,useEffect 中所有随时间变化并在内部使用的变量都应包含在数组中,如下所示:

     useEffect(() => {
          document.addEventListener('keydown', (e) => { handleKeyPress(e) });
          setIsCheckingAnswer(false);
          return () => {
            document.removeEventListener('keydown', (e) => { handleKeyPress(e) });
          }
        }, [props.roundData])
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-07-11
      • 2018-10-10
      • 2019-11-08
      • 2021-03-30
      • 1970-01-01
      • 2021-04-12
      • 2020-05-25
      • 1970-01-01
      相关资源
      最近更新 更多