用useState和setInterval反应useEffect

时间:2019-09-27 11:07:05

标签: reactjs react-hooks

使用以下代码通过组件DOM旋转对象数组。问题是状态永远不会更新,我不能锻炼为什么..?

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

const PremiumUpgrade = (props) => {
    const [benefitsActive, setBenefitsActive] = useState(0)


// Benefits Details
const benefits = [
    {
        title: 'Did they read your message?',
        content: 'Get more Control. Find out which users have read your messages!',
        color: '#ECBC0D'
    },
    {
        title: 'See who’s checking you out',
        content: 'Find your admirers. See who is viewing your profile and when they are viewing you',
        color: '#47AF4A'
    }
]


// Rotate Benefit Details
useEffect(() => {
    setInterval(() => {
        console.log(benefits.length)
        console.log(benefitsActive)

        if (benefitsActive >= benefits.length) {
            console.log('................................. reset')
            setBenefitsActive(0)
        } else {
            console.log('................................. increment')
            setBenefitsActive(benefitsActive + 1)
        }
    }, 3000)
}, [])

我得到的输出如下图所示。我可以看到useState'setBenefitsActive'被调用,但'benefitsActive'从未更新。

enter image description here

4 个答案:

答案 0 :(得分:6)

您没有将任何依赖项传递给useEffect,这意味着它只会运行一次,因此setInterval的参数将永远只会收到benefitsActive的初始值(在这种情况下,该初始值是0)。

您可以使用函数而不是仅通过设置值来修改现有状态。

setBenefitsActive(v => v + 1);

答案 1 :(得分:1)

一些代码对您有利! 在@James建议的使用效果中,将依赖项添加到要更新的变量中。另外,请不要忘记清理间隔以避免内存泄漏!

// Rotate Benefit Details
useEffect(() => {
    let rotationInterval = setInterval(() => {
        console.log(benefits.length)
        console.log(benefitsActive)

        if (benefitsActive >= benefits.length) {
            console.log('................................. reset')
            setBenefitsActive(0)
        } else {
            console.log('................................. increment')
            setBenefitsActive(benefitsActive + 1)
        }
    }, 3000)

    //Cleanup can be done like this
    return () => {
        clearInterval(rotationInterval);
    }
}, [benefitsActive]) // Add dependencies here 

工作沙箱:https://codesandbox.io/s/react-hooks-interval-demo-p1f2n

编辑

James指出,通过setTimeout可以更好地实现这一点,该实现更加简洁。

// Rotate Benefit Details
useEffect(() => {
    let rotationInterval = setTimeout(() => {
        console.log(benefits.length)
        console.log(benefitsActive)

        if (benefitsActive >= benefits.length) {
            console.log('................................. reset')
            setBenefitsActive(0)
        } else {
            console.log('................................. increment')
            setBenefitsActive(benefitsActive + 1)
        }
    }, 3000)


}, [benefitsActive]) // Add dependencies here 

由于每次setTimeout创建一个闭合循环后都会调用useEffect,因此会自动创建一种间隔。

  

如果您仍然希望使用间隔,但必须进行清理,以避免内存泄漏。

答案 2 :(得分:1)

当您将函数传递给setInterval时,您将创建一个闭包,该闭包将记住BenefitsActive的初始值。解决这个问题的一种方法是使用ref:

  const benefitsActive = useRef(0);

  // Rotate Benefit Details
  useEffect(() => {
    const id = setInterval(() => {
      console.log(benefits.length);
      console.log(benefitsActive.current);

      if (benefitsActive.current >= benefits.length) {
        console.log("................................. reset");
        benefitsActive.current = 0;
      } else {
        console.log("................................. increment");
        benefitsActive.current += 1;
      }
    }, 3000);

    return () => clearInterval(id);
  }, []);

演示:https://codesandbox.io/s/delicate-surf-qghl6

答案 3 :(得分:0)

我遇到了同样的问题,并在

上找到了完美的解决方案

https://overreacted.io/making-setinterval-declarative-with-react-hooks/

使用自己的钩子

import { useRef, useEffect } from "react";

export function useInterval(callback, delay) {
  const savedCallback = useRef();

  useEffect(() => {
    savedCallback.current = callback;
  }, [callback]);

  useEffect(() => {
    function tick() {
      savedCallback.current();
    }
    if (delay !== null) {
      const id = setInterval(tick, delay);
      return () => clearInterval(id);
    }
  }, [delay]);
}

喜欢它

useInterval(() => {
  // Your custom logic here
  setCount(count + 1);
}, 1000);