如何在React Native中延迟后隐藏组件

fykwrbwg  于 2022-11-17  发布在  React
关注(0)|答案(1)|浏览(166)

我正在试着做一个计时器组件,它会在延迟后隐藏自己。它可以工作,但当计时器消失时,我会有一个警告,我不知道如何处理它。
警告:呈现其他组件(Chronometer)时无法更新组件(WorkoutScreen)。若要在Chronometer内找到错误的setState()调用
WorkoutScreen.tsx

const WorkoutScreen = ({
  navigation,
  route,
}: RootStackScreenProps<"Workout">) => {
  const [inRest, setInRest] = useState(false)
  const [restTime, setRestTime] = useState(5)

  //I pass it to child
  const handleEndRestTime = () => {
    setInRest(false)
  }
  //

  return (
    <Layout style={styles.container}>
      <Button
        onPress={() => {
          setInRest(!inRest)
        }}
      >
        Trigger chronometer
      </Button>
      {inRest && (
        <Chronometer onEnd={handleEndRestTime} seconds={restTime}></Chronometer>
      )}
    </Layout>
  )
}

Chronometer.tsx

const Chronometer = ({ seconds, onEnd }: Props) => {
  const [timer, setTimer] = useState<number>(seconds)
  const [pause, setPause] = useState(false)
  const [running, setRunning] = useState(true)

  useEffect(() => {
    let interval: NodeJS.Timer
    if (pause === true || running === false) {
      ;() => clearInterval(interval)
    } else {
      interval = setInterval(() => {
        setTimer((timer) => timer - 1)
      }, 1000)
    }
    return () => {
      clearInterval(interval)
    }
  }, [pause, running])

  if (timer === 0 && running === true) {
    setRunning(false)
    //From parent
    onEnd()
    //
  }

  return (
    <View style={styles.container}>
      <View style={styles.chronometer}>
        <View style={styles.controls}>
          <Text>{formatHhMmSs(timer)}</Text>
        </View>
        <Button
          onPress={() => {
            setPause(!pause)
          }}
        >
          Pause
        </Button>
      </View>
    </View>
  )
}

当我删除“{inRest &&“时,警告消失。
在未来,我希望用户可以根据自己的需要重新启动计时器
提前感谢!
Warning on my emulator (1)
Warning on my emulator (2)
Warning on my emulator (3)
Warning on my terminal

9udxz4iz

9udxz4iz1#

有两个状态更新同时发生,并且与React呈现UI reconciliation冲突
1.计时器结束时,Chronometer组件内的setRunning(false)将重新呈现此组件。
1.计时器结束时,WorkoutScreen组件内的setInRest(false)也将重新呈现。
这两种重新呈现在同一计时器中发生,并且WorkoutScreen重新呈现由子组件触发。
解决方案是避免由子组件引起的父组件内部的状态改变。

const WorkoutScreen = ({
  navigation,
  route,
}: RootStackScreenProps<"Workout">) => {
  const [restTime, setRestTime] = useState(5);

  //I pass it to child
  const handleEndRestTime = () => {
    // Handle logic when workout time end
  };
  //

  return (
    <Layout style={styles.container}>
      <Chronometer onEnd={handleEndRestTime} seconds={restTime}></Chronometer>
    </Layout>
  );
};

const Chronometer = ({ seconds, onEnd }: Props) => {
  const [timer, setTimer] = useState < number > seconds;
  const [pause, setPause] = useState(false);
  const [running, setRunning] = useState(true);
  const [inRest, setInRest] = useState(false);

  useEffect(() => {
    let interval: NodeJS.Timer;
    if (pause === true || running === false) {
      () => clearInterval(interval);
    } else {
      interval = setInterval(() => {
        setTimer((timer) => timer - 1);
      }, 1000);
    }
    return () => {
      clearInterval(interval);
    };
  }, [pause, running]);

  if (timer === 0 && running === true) {
    setRunning(false);
    setInRest(false);
    //From parent
    onEnd();
    //
  }

  return (
    <View style={styles.container}>
      <Button
        onPress={() => {
          setInRest(!inRest);
        }}
      >
        Trigger chronometer
      </Button>

      {inRest && (
        <View style={styles.chronometer}>
          {/* Show Timer counter only when is running */}
          {running && (
            <View style={styles.controls}>
              <Text>{formatHhMmSs(timer)}</Text>
            </View>
          )}

          <Button
            onPress={() => {
              setPause(!pause);
            }}
          >
            {running ? "Pause" : "Start"}
          </Button>
        </View>
      )}
    </View>
  );
};

相关问题