【问题标题】:'useEffect' hook only fires once?'useEffect' 钩子只触发一次?
【发布时间】:2020-05-14 23:45:51
【问题描述】:

我正在为“React Native”开发一个猜谜游戏,用户输入一个数字,然后手机会尝试猜测它。每次手机产生猜测时,用户都可以单击“大”/“小”。当用户输入数字和计算机猜测的结果相等时,我们将进入游戏结束屏幕。

游戏结束屏幕未渲染。在屏幕上渲染游戏的逻辑放在 useEffect() 中

问题

useEffect 只在安装阶段触发一次,以后再也不会触发?

  const { userSelectedNumber, onGameOver } = props;
  useEffect(() => {
    console.log(currentGuess, userSelectedNumber);
    if (currentGuess === userSelectedNumber) {
      onGameOver(rounds);
    }
  }, [userSelectedNumber, onGameOver]);*emphasized text*

(./screens/GameScreen.js)

currentGuess === userSelectedNumber 时我们应该退出 GameScreen,但这段代码只运行一次。

GameScreen 的完整代码如下:

import React, { useState, useRef, useEffect } from "react";
import { View, StyleSheet, Button, Text, Alert } from "react-native";

import NumberContainer from "../components/NumberContainer";
import Card from "../components/Card";

const randNumberGeneratorBetween = (min, max, exclude) => {
  min = Math.ceil(min);
  max = Math.floor(max);

  const randNum = Math.floor(Math.random() * (max - min)) + min;


  if (randNum === exclude) {
    return randNumberGeneratorBetween(1, 100, exclude);
  } else {
    return randNum;
  }
};

const GameScreen = props => {
  const [currentGuess, setCurrentGuess] = useState(
    randNumberGeneratorBetween(1, 100, props.userSelectedNumber)
  );
  const [rounds, setRounds] = useState(0);

  const currentLow = useRef(1);
  const currentHigh = useRef(100);

  const { userSelectedNumber, onGameOver } = props;

  useEffect(() => {
    console.log(currentGuess, userSelectedNumber);
    if (currentGuess === userSelectedNumber) {
      onGameOver(rounds);
    }
  }, [userSelectedNumber, onGameOver]);

  const nextGuessHandler = direction => {
    if (
      (direction === "lower" && currentGuess < props.userSelectedNumber) ||
      (direction === "greater" && currentGuess > props.userSelectedNumber)
    ) {
      Alert.alert("Don't Lie", "You know this is wrong", [
        { text: "Sorry", style: "cancel" }
      ]);
    }

    if (direction === "lower") {
      currentHigh.current = currentGuess;
    } else {
      currentLow.current = currentGuess;
    }
    const nextNumber = randNumberGeneratorBetween(
      currentLow.current,
      currentHigh.current,
      currentGuess
    );
    console.log('nextNumber',nextNumber);
    setCurrentGuess(nextNumber);

    setRounds(currRounds => currRounds + 1);
    console.log('currRound',rounds);

  };

  return (
    <View style={styles.screen}>
      <Text>Opponents Guess</Text>
      <NumberContainer>{currentGuess}</NumberContainer>
      <Card style={styles.buttonContainer}>
        <Button
          title="Lower"
          onPress={nextGuessHandler.bind(this, "lower")}
        ></Button>
        <Button
          title="Greater"
          onPress={nextGuessHandler.bind(this, "greater")}
        ></Button>
      </Card>
    </View>
  );
};

const styles = StyleSheet.create({
  screen: {
    flex: 1,
    padding: 10,
    alignItems: "center"
  },
  buttonContainer: {
    flexDirection: "row",
    justifyContent: "space-between",
    marginTop: 20,
    width: 300,
    maxWidth: "80%"
  }
});

export default GameScreen;

项目可以在这里找到: https://codesandbox.io/s/github/SMasood1/guessingGame?file=/screens/GameScreen.js:852-1039

【问题讨论】:

标签: javascript reactjs react-native


【解决方案1】:

需要在useEffect hook中的dependencies数组中添加roundscurrentGuess

 useEffect(() => {
    console.log(currentGuess, userSelectedNumber);
    if (currentGuess === userSelectedNumber) {
      onGameOver(rounds);
    }
  }, [userSelectedNumber, onGameOver,currentGuess,rounds]);

使用 props 初始化状态也被认为是一种反模式,所以我建议添加另一个 useEffect 钩子:

useEffect(()=>{
    setCurrentGuess(randNumberGeneratorBetween(1, 100, props.userSelectedNumber))

},[props.userSelectedNumber]);

【讨论】:

    【解决方案2】:

    useEffect 挂钩会导致组件在依赖数组的任何值发生更改时更新。确保用于触发该钩子的值实际上正在发生变化。

    【讨论】:

      【解决方案3】:

      您可以通过在navigation.navigate 调用上提供时间戳来优雅地触发 useEffect

      例如

      // someComponent.tsx
      
      navigation.navigate('Home', {
        showSubscriptionModal: true
      })
      
      // HomeScreen.tsx
      const showSubscriptionModal = props.route.params?.showSubscriptionModal ?? false
      
      useEffect(() => {
        if(showSubscriptionModal) setIsShowingModal(true)
      },[showSubscriptionModal])
      

      只会触发一次,而

      // someComponent.tsx
      
      navigation.navigate('Home', {
        showSubscriptionModal: true,
        updateTs: new Date()
      })
      
      // HomeScreen.tsx
      const showSubscriptionModal = props.route.params?.showSubscriptionModal ?? false
      
      useEffect(() => {
        if(props.route.params?.showSubscriptionModal) setIsShowingModal(true)
      },[showSubscriptionModal, props.route.params?.updateTs])
      

      每次您通过 navigation.navigate() 重新导航到屏幕时都会触发

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2022-08-18
        • 2021-10-09
        • 1970-01-01
        • 2021-07-18
        • 1970-01-01
        • 1970-01-01
        • 2017-05-30
        • 2020-07-23
        相关资源
        最近更新 更多