【问题标题】:setInterval in a React appReact 应用程序中的 setInterval
【发布时间】:2016-07-17 21:16:35
【问题描述】:

我对 React 还很陌生,但我一直在慢慢磨练,遇到了一些我一直坚持的问题。

我正在尝试在 React 中构建一个“计时器”组件,老实说,我不知道我这样做是否正确(或有效)。在下面的代码中,我将状态设置为返回一个对象{ currentCount: 10 },并且一直在玩弄componentDidMountcomponentWillUnmountrender,我只能让状态从 10 到 9“倒计时”。

两部分问题:我做错了什么?而且,有没有更有效的方法来使用 setTimeout(而不是使用 componentDidMountcomponentWillUnmount)?

提前谢谢你。

import React from 'react';

var Clock = React.createClass({

  getInitialState: function() {
    return { currentCount: 10 };
  },

  componentDidMount: function() {
    this.countdown = setInterval(this.timer, 1000);
  },

  componentWillUnmount: function() {
    clearInterval(this.countdown);
  },

  timer: function() {
    this.setState({ currentCount: 10 });
  },

  render: function() {
    var displayCount = this.state.currentCount--;
    return (
      <section>
        {displayCount}
      </section>
    );
  }

});

module.exports = Clock;

【问题讨论】:

  • bind(this) 不再需要,react 现在自己做。
  • 你的定时器方法没有更新 currentCount
  • @Derek 你确定吗?我刚刚通过添加 this.timer.bind(this) 让我的工作正常,因为 this.timer 本身不起作用
  • @Theworm @Derek 错了,有点。 React.createClass(已弃用)自动绑定方法,但 class Clock extends Component 不会自动绑定。因此,是否需要绑定取决于您如何创建组件。

标签: javascript reactjs settimeout state


【解决方案1】:

如果您正在使用 Dan Abramov useInterval 钩子并想手动取消当前间隔,您只需再次调用钩子将 null 作为 delay 变量。

你可以在这里查看一个工作示例https://codesandbox.io/s/useinterval-cancel-interval-dan-abramov-extended-oe45z?file=/src/index.js

【讨论】:

    【解决方案2】:

    简单的做法是将其添加到窗口变量中。

    useEffect(() => {
        window.interval23 = setInterval(
          () => setState('something'),
          2500
        )
        return () => {
          clearInterval(window.interval23)
        }
     }, [])
    

    但请确保您使用 window 变量创建的任何内容都尽可能保持其唯一性,因为如果该变量已经存在,则 window 变量可能会在库中中断。

    【讨论】:

      【解决方案3】:

      使用 React Hooks 管理 setInterval:

        const [seconds, setSeconds] = useState(0)
      
        const interval = useRef(null)
      
        useEffect(() => { if (seconds === 60) stopCounter() }, [seconds])
      
        const startCounter = () => interval.current = setInterval(() => {
          setSeconds(prevState => prevState + 1)
        }, 1000)
      
        const stopCounter = () => clearInterval(interval.current)
      

      【讨论】:

        【解决方案4】:

        如果有人正在寻找实现 setInterval 的 React Hook 方法。 Dan Abramov 在他的blog 上谈到了它。如果您想深入了解该主题,包括 Class 方法,请查看它。基本上,代码是一个自定义 Hook,它将 setInterval 变为声明性。

        function useInterval(callback, delay) {
          const savedCallback = useRef();
        
          // Remember the latest callback.
          useEffect(() => {
            savedCallback.current = callback;
          }, [callback]);
        
          // Set up the interval.
          useEffect(() => {
            function tick() {
              savedCallback.current();
            }
            if (delay !== null) {
              let id = setInterval(tick, delay);
              return () => clearInterval(id);
            }
          }, [delay]);
        }
        

        为方便起见,还发布了 CodeSandbox 链接:https://codesandbox.io/s/105x531vkq

        【讨论】:

        • 这个例子很适合用 React 实现 setInterval,谢谢。
        【解决方案5】:

        在 react 类中每秒更新一次状态。请注意,我的 index.js 传递了一个返回当前时间的函数。

        import React from "react";
        
        class App extends React.Component {
          constructor(props){
            super(props)
        
            this.state = {
              time: this.props.time,
        
            }        
          }
          updateMe() {
            setInterval(()=>{this.setState({time:this.state.time})},1000)        
          }
          render(){
          return (
            <div className="container">
              <h1>{this.state.time()}</h1>
              <button onClick={() => this.updateMe()}>Get Time</button>
            </div>
          );
        }
        }
        export default App;
        

        【讨论】:

          【解决方案6】:

          我发现您的代码存在 4 个问题:

          • 在您的计时器方法中,您始终将当前计数设置为 10
          • 您尝试在渲染方法中更新状态
          • 您没有使用setState 方法来实际更改状态
          • 您没有将 intervalId 存储在状态中

          让我们尝试解决这个问题:

          componentDidMount: function() {
             var intervalId = setInterval(this.timer, 1000);
             // store intervalId in the state so it can be accessed later:
             this.setState({intervalId: intervalId});
          },
          
          componentWillUnmount: function() {
             // use intervalId from the state to clear the interval
             clearInterval(this.state.intervalId);
          },
          
          timer: function() {
             // setState method is used to update the state
             this.setState({ currentCount: this.state.currentCount -1 });
          },
          
          render: function() {
              // You do not need to decrease the value here
              return (
                <section>
                 {this.state.currentCount}
                </section>
              );
          }
          

          这将导致计时器从 10 减少到 -N。如果您希望计时器减少到 0,您可以使用稍微修改的版本:

          timer: function() {
             var newCount = this.state.currentCount - 1;
             if(newCount >= 0) { 
                 this.setState({ currentCount: newCount });
             } else {
                 clearInterval(this.state.intervalId);
             }
          },
          

          【讨论】:

          • 谢谢。这很有意义。我仍然是一个初学者,我正试图了解状态是如何工作的,以及哪些“块”中的内容,比如渲染。
          • 我想知道,是否有必要使用 componentDidMount 和 componentWillUnmount 来实际设置间隔?编辑:刚刚看到你最近的编辑。 :)
          • @Jose 我认为componentDidMount 是触发客户端事件的正确位置,所以我会用它来启动倒计时。您还在考虑其他什么初始化方法?
          • 没有必要将 setInterval 值存储为状态的一部分,因为它不会影响渲染
          • 在 ES6 中,别忘了绑定类来访问这个:this.timer.bind(this)timer() { ... }
          【解决方案7】:

          感谢@dotnetom,@greg-herbowicz

          如果它返回“this.state is undefined”——绑定定时器函数:

          constructor(props){
              super(props);
              this.state = {currentCount: 10}
              this.timer = this.timer.bind(this)
          }
          

          【讨论】:

            【解决方案8】:

            使用 Hooks 更新了 10 秒倒计时(一项新功能提案,可让您在不编写类的情况下使用状态和其他 React 功能。它们目前在 React v16.7.0-alpha 中)。

            import React, { useState, useEffect } from 'react';
            import ReactDOM from 'react-dom';
            
            const Clock = () => {
                const [currentCount, setCount] = useState(10);
                const timer = () => setCount(currentCount - 1);
            
                useEffect(
                    () => {
                        if (currentCount <= 0) {
                            return;
                        }
                        const id = setInterval(timer, 1000);
                        return () => clearInterval(id);
                    },
                    [currentCount]
                );
            
                return <div>{currentCount}</div>;
            };
            
            const App = () => <Clock />;
            
            ReactDOM.render(<App />, document.getElementById('root'));
            

            【讨论】:

            • 在 React 16.8 中,React Hooks 在稳定版本中可用。
            • "setInterval" 旨在每隔“间隔”时间运行一些周期性操作。在这里,它被用作 setTimeout,因为它是在每个滴答声中创建的。对于时钟,最好在开始时调用一次 setInterval。
            【解决方案9】:

            使用class Clock extends Component更新了 10 秒倒计时

            import React, { Component } from 'react';
            
            class Clock extends Component {
              constructor(props){
                super(props);
                this.state = {currentCount: 10}
              }
              timer() {
                this.setState({
                  currentCount: this.state.currentCount - 1
                })
                if(this.state.currentCount < 1) { 
                  clearInterval(this.intervalId);
                }
              }
              componentDidMount() {
                this.intervalId = setInterval(this.timer.bind(this), 1000);
              }
              componentWillUnmount(){
                clearInterval(this.intervalId);
              }
              render() {
                return(
                  <div>{this.state.currentCount}</div>
                );
              }
            }
            
            module.exports = Clock;
            

            【讨论】:

              猜你喜欢
              • 2021-09-08
              • 1970-01-01
              • 2019-01-30
              • 1970-01-01
              • 1970-01-01
              • 2020-04-20
              • 2020-02-24
              • 1970-01-01
              • 2016-05-14
              相关资源
              最近更新 更多