当useState值更改时,React组件中的setInterval被复制

时间:2019-03-30 21:35:05

标签: reactjs

我正在将数组传递给组件。我想每秒console.log数组中的下一项。

在下面的代码中,控制台日志的数量每秒增加一倍:

const Component = ({ someArray }) => {
  const [position, setPosition] = React.useState(0);

  setInterval(() => {
      setPosition(position + 1);
      console.log(someArray[position]);
  }, 1000);

我认为这是因为组件在position状态更改时会重新渲染,但是我不确定解决方案是什么。

4 个答案:

答案 0 :(得分:0)

防止重复setInterval调用的简单方法(如您所说,在每个渲染器上正确调用):

let clearInterval;
const Component = ({ someArray }) => {
  const [position, setPosition] = React.useState(0);

  if (!clearInterval) {
    clearInterval = setInterval(() => {
      setPosition(position + 1);
      console.log(someArray[position]);
    }, 1000);
  }

编辑:

好吧-看来您不能这样调用setPosition。 如果使用标准组件,则容易得多:

class App extends React.Component {
  state = { position: 0 };
  componentDidMount() {
    setInterval(() => {
      this.setState({ position: this.state.position + 1 });
    }, 1000);
  }
  render() {
    return <button onClick={() => {}}>{this.state.position}</button>;
  }
}

https://codesandbox.io/s/q3pzw3623q

答案 1 :(得分:0)

对我来说,从https://reactjs.org/docs/hooks-effect.html来看,您似乎想这样做

useEffect(() => {
    setInterval(() => {
        setPosition(position + 1);
        console.log(someArray[position]);
    }, 1000);
}, []);

答案 2 :(得分:0)

由于使用的是React钩子,因此需要结合使用useRef和useEffect-可以在需要时将prevProps与currentProps和clearInterval进行比较。

function usePrevious(value) {
  const ref = React.useRef();
  React.useEffect(() => {
    ref.current = value;
  });
  return ref.current;
}

const Component = ({ someArray }) => {
  const [position, setPosition] = React.useState(0);
  const prevArray = usePrevious({ someArray });
  React.useEffect(() => {
    const timer = setInterval(() => {
      setPosition(prevPosition => prevPosition + 1);
    }, 1000);
    if (prevArray && someArray && prevArray.someArray !== someArray || !someArray[position]) {
      clearInterval(timer);
    }
    return () => clearInterval(timer);
  });
  
  return (
    <div>Position: {position}</div>
  );
}

ReactDOM.render(<Component someArray={['1', '2', '3', '4']} />, document.querySelector('#app'));
<script src="https://unpkg.com/react@16.7.0-alpha.0/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16.7.0-alpha.0/umd/react-dom.development.js"></script>

<div id="app"></div>

让我知道这是否有帮助!

答案 3 :(得分:0)

正如Countingstuff指出的那样,React核心团队对此发表了广泛的博客文章: https://overreacted.io/making-setinterval-declarative-with-react-hooks/

创建一个useInterval挂钩:

import React, { useState, useEffect, useRef } from "react";

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]);
}

然后useInterval可以代替setInterval:

const Component = ({ someArray }) => {
  const [position, setPosition] = React.useState(0);

  setInterval(() => {
      useInterval(position + 1);
      console.log(someArray[position]);
  }, 1000);