簡體   English   中英

clearInterval() 在 React Native 中不起作用 - 嘗試從 class 轉換為功能組件 - 計時器

[英]clearInterval() not working in React Native - trying to convert from class to functional components - Timer

嘗試在本機反應中制作秒表我有 class 組件的工作代碼,但是在嘗試使用功能組件時clearInterval() function 不起作用

修改前來自 ReactNativeAcademy/Stopwatch 的源代碼https://github.com/ReactNativeAcademy/Stopwatch/blob/master/App.js

I need just a basic timer without laps with only start / resume / stop / reset buttons snack url for my code: https://snack.expo.io/@mansouriala/nervous-mixed-nuts in order to test it in class based您可以在功能之一下找到基於 function 注釋的每個 class 組件。

我不知道,但可能的一種解決方案是將 setInterval 包裝在 useEffect 中,然后創建一個新的 state 變量,在啟動時將其切換為 true,並且 useEffect 偵聽該變量。

事不宜遲,代碼如下:

import React, { Component, useEffect, useState } from 'react';
import { StyleSheet, Text, View, TouchableOpacity } from 'react-native';
import moment from 'moment';

function Timer({ interval, style }) {
  const pad = (n) => (n < 10 ? `0${n}` : n);
  const duration = moment.duration(interval);
  const centiseconds = Math.floor(duration.milliseconds() / 10);

  return (
    <View style={styles.timerContainer}>
      <Text style={style}>{pad(duration.minutes())}:</Text>
      <Text style={style}>{pad(duration.seconds())},</Text>
      <Text style={style}>{pad(centiseconds)}</Text>
    </View>
  );
}

function RoundButton({ title, color, background, onPress }) {
  return (
    <TouchableOpacity onPress={onPress} style={[styles.button, { backgroundColor: background }]}>
      <View style={styles.buttonBorder}>
        <Text style={[styles.buttonTitle, { color }]}>{title}</Text>
      </View>
    </TouchableOpacity>
  );
}

function ButtonsRow({ children }) {
  return <View style={styles.buttonsRow}>{children}</View>;
}

// export default class App extends Component {
export default function App() {
  const [timer, setTimer] = useState(0);
  const [state, setState] = useState({
    start: 0,
    now: 0,
    currentTime: 0,
  });

  // constructor(props) {
  //   super(props);
  //   this.state = {
  //     start: 0,
  //     now: 0,
  //     currentTime: 0,
  //   };
  // }

  useEffect(() => {
    return () => {
      clearInterval(timer);
    };
  }, []);

  // componentWillUnmount() {
  //   clearInterval(this.timer);
  // }

  const startHandler = () => {
    const now = new Date().getTime();
    setState({
      start: now,
      now,
      currentTime: 0,
    });
    setInterval(
      setInterval(() => {
        setState((prev) => ({ ...prev, now: new Date().getTime() }));
      }, 100)
    );
  };

  // startHandler = () => {
  //   const now = new Date().getTime();
  //   this.setState({
  //     start: now,
  //     now,
  //     currentTime: 0,
  //   });
  //   this.timer = setInterval(() => {
  //     this.setState({ now: new Date().getTime() });
  //   }, 100);
  // };

  const stopHandler = () => {
    clearInterval(timer);
    const { currentTime, now, start } = state;
    setState((prev) => ({
      // ...prev,
      currentTime: currentTime + (now - start),
      start: 0,
      now: 0,
    }));
  };

  // stopHandler = () => {
  //   clearInterval(this.timer);
  //   const { currentTime, now, start } = this.state;
  //   this.setState({
  //     currentTime: currentTime + now - start,
  //     start: 0,
  //     now: 0,
  //   });
  // };

  const resetHandler = () => {
    setState({
      currentTime: 0,
      start: 0,
      now: 0,
    });
  };

  // resetHandler = () => {
  //   this.setState({
  //     currentTime: 0,
  //     start: 0,
  //     now: 0,
  //   });
  // };

  const resumeHandler = () => {
    const now = new Date().getTime();
    setState({
      start: now,
      now,
    });
    setTimer(
      setInterval(() => {
        setState((prev) => ({ ...prev, now: new Date().getTime() }));
      }, 100)
    );
  };

  // resumeHandler = () => {
  //   const now = new Date().getTime();
  //   this.setState({
  //     start: now,
  //     now,
  //   });
  //   this.timer = setInterval(() => {
  //     this.setState({ now: new Date().getTime() });
  //   }, 100);
  // };

  // render() {
  const { now, start, currentTime } = state;
  // const { now, start, currentTime } = this.state;

  return (
    <View style={styles.container}>
      <Timer interval={currentTime + (now - start)} style={styles.timer} />
      <ButtonsRow>
        <RoundButton title={'Start'} color={'#50D167'} background={'#1B361F'} onPress={startHandler} />
        <RoundButton title={'Stop'} color={'#E33935'} background={'#3C1715'} onPress={stopHandler} />
        <RoundButton title={'Reset'} color={'#FFFFFF'} background={'#3D3D3D'} onPress={resetHandler} />
        <RoundButton title={'Resume'} color={'#50D167'} background={'#1B361F'} onPress={resumeHandler} />
      </ButtonsRow>
    </View>
  );
}
// }

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#0D0D0D',
    alignItems: 'center',
    paddingTop: 130,
    paddingHorizontal: 20,
  },
  timer: {
    color: '#FFFFFF',
    fontSize: 76,
    fontWeight: '200',
    width: 110,
  },
  button: {
    width: 80,
    height: 80,
    borderRadius: 40,
    justifyContent: 'center',
    alignItems: 'center',
  },
  buttonTitle: {
    fontSize: 18,
  },
  buttonBorder: {
    width: 76,
    height: 76,
    borderRadius: 38,
    borderWidth: 1,
    justifyContent: 'center',
    alignItems: 'center',
  },
  buttonsRow: {
    flexDirection: 'row',
    alignSelf: 'stretch',
    justifyContent: 'space-between',
    marginTop: 80,
    marginBottom: 30,
  },
  timerContainer: {
    flexDirection: 'row',
  },
});

試試看:

useEffect(() => {    
  clearInterval(timer);  
}, []);

在 useEffect 中使用return時,代碼僅在組件卸載時觸發。

不要在 state 中存儲間隔 id 之類的東西,因為每次更新都會重新渲染。 如果您功能正常,請使用setInterval實現useRef() ,如果基於 class ,請使用this.interval

另一個問題是在 ref 的功能組件中調用clearInterval() ,而不是.current

這是我剛剛調試的一個片段:

  const spinnerCount = useRef(0)
  const interval = useRef(null)

  useEffect(() => {
    if (withProgress && inProgress && notification == '') {
      interval.current = setInterval(() => {
        if (spinnerCount.current >= 40) {
          clearInterval(interval.current)
          spinnerCount.current = 0
          setNotification('Something happened... Please try again.')
        } else {
          spinnerCount.current = spinnerCount.current + 1
        }
      }, 1000)
    } 
    if (notification !== '' && inProgress === false) {
      const delay = notification.length > 100 ? 6000 : 3000
      setTimeout(() => {
        clearInterval(interval.current)
        spinnerCount.current = 0
        setNotification('');
      }, delay);
    }
  }, [inProgress])

里面有一些額外的東西,基本上這是一個消失的通知組件,它還具有一個進度微調器。 在這種情況下,如果組件正在顯示微調器,但從未觸發成功/錯誤通知,則微調器將在 40 秒后自動退出。 因此間隔/微調器計數

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM