无法在React本机应用程序中清除setInterval [英] Unable to clear setInterval in react native app

查看:51
本文介绍了无法在React本机应用程序中清除setInterval的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个非常简单的应用程序,正在尝试实现倒数计时,开始 Stop 功能似乎运行良好,但是当我按 Stop seconds 的值不会在视图中更新,这是理想的行为,但是当我观察到控制台日志时,它显示了 sec 的值不断变化,猜测表明 setInterval 仍在运行且未清除.

I have this very simple app, where I am trying to implement countdown, Start and Stop functionality seems to be working fine but when I press Stop, the value of seconds is not updating in the view which is desired behaviour but when I observed console log, it it showing the value of sec continuously changing which I guess shows that setInterval is still running and not cleared.

这是随附的代码:

import React, { useState, useEffect } from "react";
import {
  StyleSheet,
  Text,
  View,
  StatusBar,
  TouchableOpacity,
  Dimensions,
} from "react-native";

const screen = Dimensions.get("screen");

export default function App() {
  const [seconds, setSeconds] = useState(4);
  const [start, setStartToggle] = useState(true);
  let [fun, setFun] = useState(null);
  const getRemaining = () => {
    const minute = Math.floor(seconds / 60);
    const second = seconds - minute * 60;
    return formatTime(minute) + ":" + formatTime(second);
  };

  const formatTime = (time) => {
    return ("0" + time).slice(-2);
  };

  const startTimer = () => {
    setStartToggle(false);

    console.log("StartTimer");
    let sec = seconds;
    setFun(
      setInterval(() => {
        console.log("akak:", sec);
        if (sec <= 0) stopTimer();
        setSeconds(sec--);
      }, 1000)
    );
  };

  const stopTimer = () => {
    setStartToggle(true);
    console.log("StopTimer");
    clearInterval(fun);
    setFun(null);
  };

  return (
    <View style={styles.container}>
      <StatusBar barStyle="light-content" />
      <Text style={styles.timerText}>{getRemaining(seconds)}</Text>
      {start ? (
        <TouchableOpacity onPress={startTimer} style={styles.button}>
          <Text style={styles.buttonText}>Start</Text>
        </TouchableOpacity>
      ) : (
        <TouchableOpacity
          onPress={stopTimer}
          style={[styles.button, { borderColor: "orange" }]}
        >
          <Text style={styles.buttonText}>Stop</Text>
        </TouchableOpacity>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#07121B",
    alignItems: "center",
    justifyContent: "center",
  },
  button: {
    borderWidth: 10,
    borderColor: "#89aaff",
    width: screen.width / 2,
    height: screen.width / 2,
    borderRadius: screen.width / 2,
    justifyContent: "center",
    alignItems: "center",
  },
  buttonText: {
    fontSize: 40,
    color: "#89aaff",
    fontWeight: "bold",
  },

  timerText: {
    fontSize: 90,
    color: "#89aaff",
    fontWeight: "bold",
    marginBottom: 20,
  },
});

应用行为:

即使在按下Stop后仍显示输出:

Output being shown even after Stop is hit:

感谢 Emiel Zuurbier lissettdm 的回答,我的代码正在运行.

Thanks to Emiel Zuurbier and lissettdm for their answers, my code is working now.

工作代码:

// import { StatusBar } from "expo-status-bar";
import React, { useState, useEffect, useRef } from "react";
import {
  StyleSheet,
  Text,
  View,
  StatusBar,
  TouchableOpacity,
  Dimensions,
} from "react-native";

const screen = Dimensions.get("screen");

export default function App() {
  const [seconds, setSeconds] = useState(11);
  const funRef = useRef(null);
  const [start, setStartToggle] = useState(true);

  const getRemaining = () => {
    const minute = Math.floor(seconds / 60);
    const second = seconds - minute * 60;
    return formatTime(minute) + ":" + formatTime(second);
  };

  const formatTime = (time) => {
    return ("0" + time).slice(-2);
  };
  let sec = seconds;
  useEffect(() => {
    // let timer = null;

    if (!start) {
      let sec = seconds;
      funRef.current = setInterval(() => {
        console.log("Seconds remaining:", sec);
        if (sec <= 0) {
          clearInterval(funRef.current);
          setStartToggle(true);
        }
        setSeconds(sec--);
      }, 1000);
    } else {
      clearInterval(funRef.current);
    }
  }, [start]);

  const startTimer = () => {
    setSeconds(sec);
    setStartToggle(false);
  };

  const stopTimer = () => {
    setSeconds(sec);
    setStartToggle(true);
  };

  return (
    <View style={styles.container}>
      <StatusBar barStyle="light-content" />
      <Text
        style={
          seconds ? styles.timerText : [styles.timerText, { color: "red" }]
        }
      >
        {getRemaining(seconds)}
      </Text>
      {start ? (
        <TouchableOpacity onPress={startTimer} style={styles.button}>
          <Text style={styles.buttonText}>Start</Text>
        </TouchableOpacity>
      ) : (
        <TouchableOpacity
          onPress={stopTimer}
          style={[styles.button, { borderColor: "orange" }]}
        >
          <Text style={styles.buttonText}>Stop</Text>
        </TouchableOpacity>
      )}
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: "#07121B",
    alignItems: "center",
    justifyContent: "center",
  },
  button: {
    borderWidth: 10,
    borderColor: "#89aaff",
    width: screen.width / 2,
    height: screen.width / 2,
    borderRadius: screen.width / 2,
    justifyContent: "center",
    alignItems: "center",
  },
  buttonText: {
    fontSize: 40,
    color: "#89aaff",
    fontWeight: "bold",
  },

  timerText: {
    fontSize: 90,
    color: "#89aaff",
    fontWeight: "bold",
    marginBottom: 20,
  },
});

推荐答案

当前,您使用 useState 挂钩存储间隔参考.每次重新渲染 App 组件时,都会重置 fun 状态,但不会与间隔完全相同.

Currently you use the useState hook to store the interval reference. At every re-render of the App component the fun state is reset, but will not be the exact same reference to the interval.

代替使用 useRef 钩子.它可以创建对间隔的引用,该间隔在重新渲染期间不会改变.这意味着引用的 current 属性中的值将始终是完全相同的.

Instead use the useRef hook. It can create a reference to the interval which will not change during re-renders. This means that the value in the current property of the reference will always be the exact same one.

最重要的是,使用 useEffect 钩子来监视何时设置或取消设置运行状态,并根据该状态启动和停止计时器.

To top it off, use the useEffect hook to watch when a running state is set or unset and start and stop the timer based on that state.

import React, { useState, useRef, useEffect } from 'react'

export default function App() {
  const [isRunning, setIsRunning] = useState(false);
  const funRef = useRef(null);

  const startTimer = () => {
    if (!isRunning) {
      setIsRunning(true);
    }
  };

  const stopTimer = () {
    if (isRunning && funRef.current !== null) {
      setIsRunning(false);
    }
  };

  useEffect(() => {
    if (isRunning) {
      funRef.current = setInterval(() => { // Save reference to interval.
        // ...
      }, 1000);
    } else {
      clearInterval(funRef.current); // Stop the interval.
    }
  }, [isRunning]);

  // ...
}

这篇关于无法在React本机应用程序中清除setInterval的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆