首页 > 解决方案 > 如何在反应中使用循环内的settimeout函数更新状态?

问题描述

我正在创建反应应用程序来可视化排序算法,我停止了这个问题。我正在从 bar 状态循环遍历数组的所有元素,并且我想交换它们(出于测试目的)。这并不像我想要的那样完美,因为它“忽略”了setTimeout功能并立即执行。我用 setBars 尝试了一些东西,但它也不起作用。我怎样才能做到这一点,以便在setTimeout函数中设置超时后发生交换?

const [bars, setBars] = useState([23, 63, 236, 17, 2]);

const swap = (arr, i, j) => {
 let temp = arr[i];
 arr[i] = arr[j];
 arr[j] = temp;
};

for (let i = 0; i < bars.length - 1; i++) {
 setTimeout(() => {
  swap(bars, i, i + 1);
 }, 2000);
}

标签: javascriptreactjs

解决方案


您希望useEffect在组件安装时使用挂钩来启动该过程:

useEffect(() => {
    // ...code here...
}, []);

最后的空依赖数组表示“仅在组件第一次安装时运行它”。

在其中,您的代码将安排计时器回调:

for (let i = 0; i < bars.length - 1; i++) {
    setTimeout(() => {
        setBars(bars => {
            // Copy the current array
            const newBars = [...bars];
            // Do the swap
            swap(newBars, i, i + 1);
            // Set the state by returning the update
            return newBars;
        });
    }, 2000 * (i + 1));
}

请注意,它使用 的回调形式setBars,以便您在当前状态下操作(而不是bars,它只是第一次挂载期间使用的数组,因为您的函数仅在第一次挂载发生时被调用)。

另请注意,间隔2000 * (i + 1)不仅仅是2000. 所以每个回调都在最后一个回调之后 2000 毫秒发生,因为它们都是同时安排的。

上面的另一个重要方面是它let在 中使用for,因此每个循环体都有自己的 i变量。(如果使用的代码var,或let在 之外for,所有回调将共享同一个i变量,该变量的值为bars.length。)


或者,我想我可能会采取这种方法:

由于您想同时更新两者i,并且bars最好在异步进行更新时使用状态设置器的回调形式(如setTimeout),我将合并ibars一个状态项(而我通常会保留它们分开):

const [barState, setBarState] = useState({i: 0, bars: [23, 63, 236, 17, 2]});

然后,当您的组件安装时,您将使用useEffect钩子启动该过程,然后在每次更改为barState

useEffect(() => {
    // ...code here...
}, [barState]);

包含在末尾的依赖项数组barState说“在组件安装和每次barState更改时运行它”。

在其中,您的代码将安排计时器回调:

if (barState.i < barState.bars.length - 1) {
    setTimeout(() => {
        setBarState(({i, bars}) => {
            // Copy the current array
            bars = [...bars];
            // Do the swap
            swap(bars, i, i + 1);
            // Set the state by returning the update
            ++i;
            return {i, bars};
        });
    }, 2000);
}

现场示例:

const { useState, useEffect } = React;

const swap = (arr, i, j) => {
    let temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
};

function Example() {
    const [barState, setBarState] = useState({i: 0, bars: [23, 63, 236, 17, 2]});

    useEffect(() => {
        if (barState.i < barState.bars.length - 1) {
            setTimeout(() => {
                setBarState(({i, bars}) => {
                    // Copy the current array
                    bars = [...bars];
                    // Do the swap
                    swap(bars, i, i + 1);
                    // Set the state by returning the update
                    ++i;
                    return {i, bars};
                });
            }, 2000);
        }
    }, [barState]);

    return <div>{barState.bars.join(", ")}</div>;
}

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


推荐阅读