我想显示"橙色";持续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
将是更好的方法。
由于setInterval
在useEffect
中被调用,初始值将被设置为改变显示的水果信息的间隔。
代码中的问题是在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>