导航到页面后,useState不触发

时间:2020-06-30 13:21:10

标签: reactjs react-native

第一次浏览Test component时,一切正常。 React Navigation 5用于导航到Results页面,然后导航回到Test component。但是,尽管我通过timerIsRunning正确地收到了route.params道具,但并未像我期望的那样设置并传递给<Timer>

我是否误解了它的工作原理?

测试组件(已删除)

export const Test = ({ navigation, route }) => {
  const { timed, category, minutes, timerIsRunning } = route.params; // data is received correctly

  const [timerRunning, setTimerRunning] = React.useState(timerIsRunning); // this works the first time it's navigated to

  return (
    <View style={{ flex: 1, margin: 10 }}>
      <ScrollView>
        {timed ? <Timer seconds={minutes*60} timerRunning={timerRunning} /> : null} // timer component called here
      </ScrollView>
     
        <Button
          onPress={() => {
            setResponseDetails({ answered: false, index: null });
            setActiveIndex(0);
            setStoredResponses([]);
            setTimerRunning(false); // this correctly stops the timer
            navigation.navigate('Results', { // and navigates to the Results page
              data: storedResponses,
              category: category,
              timed: timed,
              minutes: minutes,
            });
          }}
          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 } = route.params;

  return (
    <View>
   
      <Button
        onPress={() =>
          navigation.navigate('Test', { // Navigates back to test
            timed: timed,
            minutes: minutes,
            category: category,
            timerIsRunning: true // Correctly passes this prop, and is received in the Test component
          })
        }
      >
        <Icon name="arrow-back" />
        <Text>Try the {category} test again</Text>
      </Button>

    </View>
  );
};

3 个答案:

答案 0 :(得分:0)

您是否尝试过将timerActive添加为对第二个useEffect的依赖项之一?


  React.useEffect(() => {
    if (timeLimit > 0 && timerActive) {
      setTimeout(() => {
        // console.log('startTime, ', timeLimit);
        settimeLimit(timeLimit - 1);
      }, 1000);
    }
    if (timeLimit === 0 && timerRunning || !timerRunning) {
      console.log('done');
    }
  }, [timeLimit, timerActive]);

答案 1 :(得分:0)

以下是一个工作计时器的示例,该计时器需要几秒钟的时间,并且timerRunning作为道具,如果需要任何帮助,请告诉我。您可以更新问题或添加有效的代码段。

const pad = (num) => `0${String(num)}`.slice(-2);
//moved formatTime out of the component
const formatTime = (timeLimit) => {
  let displaySeconds = timeLimit % 60;
  let minutes = Math.floor(timeLimit / 60);
  //clean up formatting number
  return `${pad(minutes)}:${pad(displaySeconds)}`;
};

const Timer = ({ seconds, timerRunning }) => {
  const [timeLimit, settimeLimit] = React.useState(seconds);
  //removed timerActive as it's a copy of timerRunning
  React.useEffect(
    () => {
      if (timerRunning) {
        //changed this to a interval and cancel it
        //  when timeLimit or timerRunning changes
        let interval;
        if (timerRunning) {
          interval = setInterval(() => {
            //using callback to get current limit value
            //  prevent using stale closure or needless
            //  dependency
            settimeLimit((timeLimit) => {
              //do the check here so timeLimit is not
              //  a dependency of this effect
              if (timeLimit !== 0) {
                return timeLimit - 1;
              }
              //do not keep running the interval
              //  if timeLimit is 0
              clearInterval(interval);
              return timeLimit;
            });
          }, 1000);
        }
        //clean up function will end interval
        return () => clearInterval(interval);
      }
    },
    //re run if seconds or timerRunning changes
    [seconds, timerRunning]
  );
  //set local timeLimit again when seconds change
  React.useEffect(() => settimeLimit(seconds), [seconds]);

  return <div> {formatTime(timeLimit)}</div>;
};

function App() {
  const [seconds, setSeconds] = React.useState(10);
  const [running, setRunning] = React.useState(false);
  return (
    <div>
      <div>
        <label>
          run timer
          <input
            type="checkbox"
            checked={running}
            onChange={() => setRunning((r) => !r)}
          />
        </label>
        <div>
          <label>
            <select
              onChange={(e) =>
                setSeconds(Number(e.target.value))
              }
              value={seconds}
            >
              <option value={1}>1</option>
              <option value={10}>10</option>
              <option value={100}>100</option>
              <option value={1000}>1000</option>
            </select>
          </label>
        </div>
      </div>
      <Timer seconds={seconds} timerRunning={running} />
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>


<div id="root"></div>

答案 2 :(得分:0)

好的,实际上我已经解决了-使用React Navigation 5.x,您可以使用route.params中的useEffect来触发动作。我结束了:

   React.useEffect(() => {
    setTimerRunning(timerIsRunning);
  }, [route.params]);