首页 > 解决方案 > 是否可以在 React 中创建一个可以在状态中传递的递归函数

问题描述

我正在尝试将存储在状态中的数组中的项目一次渲染到屏幕上。在屏幕上显示一个项目后,它将从数组中删除,然后显示下一个项目,依此类推,直到数组中没有任何项目。让我大吃一惊的是,当函数已经运行时,可以通过 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])

无论如何我可以实现我所追求的吗?

标签: reactjs

解决方案


您可以使用setTimeout. 如果队列中有一个项目,则显示它并重新启动超时。下面的示例不是 React 特定的,但它演示了基本思想,包括在事物运行时将项目添加到队列中。

// initial data
const items = [1,2,3,4,5,6];

// get references to dom elements
const appended = document.querySelector('#appended');
const arrayElem = document.querySelector('#array');
const button = document.querySelector('button');

// keep track of the timeout id just so we know
// whether it's currently running.
let timeoutId;

// add an item to the queue when the button is clicked.
button.addEventListener('click', () => {
  items.push((items[items.length - 1] || 0) + 1);
  arrayElem.innerText = JSON.stringify(items);
  
  // if the timeout isn't already running, start it.
  if (!timeoutId) {
    update();
  }
});


function update (delay = 1000) {
  // no items left? clear timeoutId and bail.
  if(!items.length) {
    timeoutId = null;
    return;
  }
  
  // get the first item in the queue
  const x = items.shift();
  
  // append it to the display
  appended.innerHTML += `<div>${x}</div>`;
  
  // update the queue display
  arrayElem.innerText = JSON.stringify(items);
  
  // restart the timeout
  timeoutId = setTimeout(update, delay);
}

update();
<button>Add Item</button>
<pre id="array"></pre>
<pre id="appended"></pre>


推荐阅读