首页 > 解决方案 > 在导航之前反应回调没有得到更新

问题描述

我确定这是可以通过 修复的情况async/await,但我看不出应该在哪里应用。

我有三个组件。一个组件是一个计时器,它接收来自父级的回调并返回计时器停止时剩余的剩余时间。父组件接收此数据并导航到结果页面。但是,导航发生在接收数据之前,因此不会传递。如果您向后导航并重复该过程,则会显示先前的数据,因此我知道它在理论上有效,但这只是等待数据到达的情况。

定时器组件

export const Timer = ({ seconds, timerRunning, checkForTimeLimit }) => {
  const [timeLimit, settimeLimit] = React.useState(seconds);
  const [timerActive, setTimerActive] = React.useState(timerRunning);

  React.useEffect(() => {
    setTimerActive(timerRunning);
    settimeLimit(seconds);
    return function cleanup() {
      setTimerActive(false);
    }
  }, [timerRunning]);

  React.useEffect(() => {
    if (timeLimit > 0 && timerActive) {
      setTimeout(() => {
        settimeLimit(timeLimit - 1);
      }, 1000);
    }
    if (timeLimit === 0 && timerRunning || !timerRunning) {
      checkForTimeLimit(timeLimit); // When the timer is stopped, it passes back the remaining time
    }
  }, [timeLimit, timerActive]);

 //...Other stuff

父组件

export const Test = ({ navigation, route }) => {
  const [timerRunning, setTimerRunning] = React.useState(timerIsRunning);
  const [secondsForTest, setSecondsForTest] = React.useState(minutes * 60);
  const [remainingTime, setRemainingTime] = React.useState(0);

  const handleCheckForTimeLimit = (timeLimit) => {
    setRemainingTime(timeLimit); // Receives the remaining time from timer and sets to state
  };

  return (
    <View style={{ flex: 1, margin: 10 }}>
      {timed ? (
        <Timer
          seconds={secondsForTest}
          timerRunning={timerRunning}
          checkForTimeLimit={(
            timeLimit // passes callback to timer
          ) => handleCheckForTimeLimit(timeLimit)}
        />
      ) : null}

      <Button
        onPress={() => {
          setTimerRunning(false); // stops timer and triggers callback
          setResponseDetails({ answered: false, index: null });
          setActiveIndex(0);
          setStoredResponses([]);
          navigation.navigate('Results', {
            // navigates to results page and passes in data
            data: storedResponses,
            category: category,
            timed: timed,
            minutes: minutes,
            remainingTime: remainingTime,
          });
        }}
        disabled={!responseDetails.answered}
        style={styles.button}
      >
        <Text>Results</Text>
        <Icon name="arrow-dropright" />
      </Button>
    </View>
  );
};

结果页面

export const Results = ({ navigation, route }) => {
  const { category, data: testData, timed, minutes, remainingTime } = route.params;
  // remainingTime is not populated
}

我应该如何更新此代码以便remainingTime填充并传递到结果页面?

标签: javascriptreactjsreact-native

解决方案


由于您的回调用于设置timeLimit,因此导航应该是应用更改的效果timeLimit(导航不应该在新闻处理程序中)。

因此,只需将您的导航包裹起来useCallback,并将其作为依赖项传递给触发timeLimit更改的效果。

更新

尝试使用附加状态以仅在按下按钮时允许导航。

export const Test = ({ navigation, route }) => {
  const [timerRunning, setTimerRunning] = React.useState(timerIsRunning);
  const [secondsForTest, setSecondsForTest] = React.useState(minutes * 60);
  const [remainingTime, setRemainingTime] = React.useState(0);

  // use additional state
  const [isNavigationAllowed, setIsNavigationAllowed] = React.useState(false);

  const handleCheckForTimeLimit = (timeLimit) => {
    setRemainingTime(timeLimit); // Receives the remaining time from timer and sets to state
  };

  // move navigation into callback
  const navigateToResult = useCallback(() => {
    navigation.navigate("Results", {
      data: storedResponses,
      category: category,
      timed: timed,
      minutes: minutes,
      remainingTime: remainingTime,
    });
  }, [storedResponses, category, timed, minutes, remainingTime]);

  // navigate only when you allow it
  useEffect(() => {
    if (isNavigationAllowed) {
      navigateToResult();
    }
  }, [navigateToResult, isNavigationAllowed]);

  return (
    <View style={{ flex: 1, margin: 10 }}>
      {timed ? (
        <Timer
          seconds={secondsForTest}
          timerRunning={timerRunning}
          checkForTimeLimit={(
            timeLimit // passes callback to timer
          ) => handleCheckForTimeLimit(timeLimit)}
        />
      ) : null}

      <Button
        onPress={() => {
          setTimerRunning(false); // stops timer and triggers callback
          setResponseDetails({ answered: false, index: null });
          setActiveIndex(0);
          setStoredResponses([]);
          // allow navigation only after button is pressed
          setIsNavigationAllowed(true);
        }}
        disabled={!responseDetails.answered}
        style={styles.button}
      >
        <Text>Results</Text>
        <Icon name="arrow-dropright" />
      </Button>
    </View>
  );
};


推荐阅读