使用React钩子每隔X秒显示一个不同的值



我想显示"橙色";持续2秒;Kiwi";或1秒;芒果"持续3秒。这是我的尝试,它显示了一个静止的";橙色:2000〃;而我期望它根据指定的秒数翻转。我错过了什么?

import React, { useState, useEffect } from 'react';
const IntervalExample = () => {
const fruits = [
{id: 1, name: "Orange", duration: 2000},
{id: 2, name: "Kiwi", duration: 1000},
{id: 3, name: "Mango", duration: 3000},
]
const [index, setIndex] = useState(0);
const [currentFruit, setCurrentFruit] = useState(fruits[index]);
useEffect(() => {
const interval = setInterval(() => {
setIndex(index === fruits.length - 1 ? 0 : index + 1)
setCurrentFruit(fruits[index])
}, currentFruit.duration);
return () => clearInterval(interval);
}, []);
return (
<div className="App">
<header className="App-header">
{currentFruit.name}: {currentFruit.duration}
</header>
</div>
);
};
export default IntervalExample;

使组件受其状态驱动,当索引更改时,它会触发一个更新currentFruit状态的useEffect挂钩,currentFruits更改会触发另一个更新索引的useEffect,依此类推,然后只需使用setTimeout,如:

const IntervalExample = () => {
const fruits = [
{id: 1, name: "Orange", duration: 2000},
{id: 2, name: "Kiwi", duration: 1000},
{id: 3, name: "Mango", duration: 3000},
]
const [index, setIndex] = useState(0);
const [currentFruit, setCurrentFruit] = useState(fruits[index]);


useEffect(() => {
setCurrentFruit(fruits[index])
}, [index])
useEffect(() => {
const interval = setTimeout(() => {
setIndex(index === fruits.length - 1 ? 0 : index + 1)       
}, currentFruit.duration);
}, [currentFruit])
return (
<div className="App">
<header className="App-header">
{currentFruit.name}: {currentFruit.duration}
</header>
</div>
);
};

UseEffect只能调用一次。因此,Const interval将在时间间隔内为函数调用初始化一个闭包。它永远不会改变。闭包中的索引将始终为0。

这是我的解决方案,只需使用一种状态作为索引:

const IntervalExample = () => {
const fruits = [
{id: 1, name: "Orange", duration: 2000},
{id: 2, name: "Kiwi", duration: 1000},
{id: 3, name: "Mango", duration: 3000},
]
const [index, setIndex] = useState(0);
//because fruit depend on index. Dont need a separate state
const currentFruit = fruits[index];
const handleIndex = () => {
setIndex(index === fruits.length - 1 ? 0 : index + 1)
}
useEffect(() => {
//after render current fruit. We new timeout to set next fruit.
setTimeout(handleIndex, currentFruit.duration);
}, [index]);
return (
<div className="App">
<header className="App-header">
{currentFruit.name}: {currentFruit.duration}
</header>
</div>
);
};

如果您想对每种水果使用不同的超时,那么setTimeout将是更好的方法。

由于setIntervaluseEffect中被调用,初始值将被设置为改变显示的水果信息的间隔。

代码中的问题是在index未正确更新的状态下更新时出现的。

为了解决这个问题,我使用更新后的状态值,而不是直接更新值,比如下面的

setIndex((index) => (index === fruits.length - 1 ? 0 : index + 1))

const { useState, useEffect } = React;
const fruits = [
{ id: 1, name: "Orange", duration: 2000 },
{ id: 2, name: "Kiwi", duration: 1000 },
{ id: 3, name: "Mango", duration: 3000 }
];
const IntervalExample = () => {
const [index, setIndex] = useState(0);
const [currentFruit, setCurrentFruit] = useState(fruits[0]);
useEffect(() => {
const interval = setInterval(() => {
setIndex((index) => (index === fruits.length - 1 ? 0 : index + 1));
}, fruits[index].duration);
return () => clearInterval(interval);
}, []);
useEffect(() => {
setCurrentFruit(fruits[index]);
}, [index]);
return (
<div className="App">
<header className="App-header">
{currentFruit.name}: {currentFruit.duration}
</header>
</div>
);
};

ReactDOM.render(
<IntervalExample />,
document.getElementById("root")
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

最新更新