我正在尝试将存储在状态中的数组中的项目一次渲染到屏幕上。当一个项目显示在屏幕上后,它将从数组中删除,然后显示下一个项目,依此类推,直到数组中没有任何项目为止。让我感到惊讶的是,当函数已经运行时,可以通过 websockets 将项目添加到数组中。所以本质上我想创建一个实时项目队列。当一个或多个项目添加到队列中时,我希望该函数能够触发并在屏幕上逐个显示项目,直到队列为空。
我认为使用最初可以由 useEffect 触发的递归函数可能会起作用。但奇怪的是,在我的 animateDisplay 函数内部,项目的值永远不会改变,但在函数外部,它会像预期的那样发生变化。这就像项目的值被缓存在递归函数中一样。所以我的函数永远重复:
console.log(`outside ${items}`) // If items started as [1,2,3] it would log [1,2,3] then [2,3] then [3] on each render
const animateDisplay = async () => {
running.current = true
setDisplay(items[0])
await timeout(4000)
setDisplay({})
setItems(prevState => prevState.slice(1))
console.log(`inside ${items}`) // If items started as [1,2,3] it would log [1,2,3] on every render
if (items.length > 0) {
await timeout(4000)
await animateDisplay()
}
running.current = false
}
useEffect(() => {
if (running.current === false && items.length > 0) {
animateDisplay()
}
}, [items])
我能实现我所追求的目标吗?
最佳答案
主要问题是外壳陈旧。既然你说你基本上是在队列中工作,那么你可以使用 React ref 和间隔计时器来访问队列的前面,并在间隔上使元素出队。
示例:
const [queue, setQueue] = useState([]);
// Ref to store current queue state reference
const queueRef = useRef();
useEffect(() => {
queueRef.current = queue;
}, [queue]);
// Effect to run the interval and manage the queue
useEffect(() => {
const timer = setInterval(() => {
// Access queue front
const [front] = queueRef.current;
// Dequeue
setQueue((queue) => queue.slice(1));
}, 4000);
return () => clearInterval(timer);
}, []);
const enqueue = () => {
setQueue((queue) => queue.concat(/* new value */));
};
更新
更新为仅在填充队列时运行队列。
const [queue, setQueue] = useState([]);
const timerRef = useRef(null);
const queueRef = useRef();
const startQueue = () => {
// process function
const tick = () => {
const [front] = queueRef.current;
if (!queueRef.current.length) {
clearInterval(timerRef.current);
timerRef.current = null;
}
front && console.log(front);
setQueue((queue) => queue.slice(1));
};
// start process
timerRef.current = setInterval(tick, 1000);
// immediately invoke
tick();
};
useEffect(() => {
// cache reference to queue
queueRef.current = queue;
// if queue populated and not running, start queue
if (queue.length && !timerRef.current) {
startQueue();
}
}, [queue]);
useEffect(() => {
// clear any running timers when unmounting
return () => clearInterval(timerRef.current);
}, []);
const enqueue = () => {
setQueue((queue) => queue.concat(count++));
};
关于reactjs - 是否可以在React中制作一个可以在状态中传递的递归函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69279362/