使用以下代码通过组件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'从未更新。
答案 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);
}, []);
答案 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);