首页 > 解决方案 > 为什么 setTimeout 在我的冒泡排序函数中不起作用(REACT)

问题描述

我正在尝试可视化我的冒泡排序算法,所以我希望算法等待 50 毫秒才能可视化它。我尝试使用 setTimeout 和 async 函数,但它不起作用,当您删除 setTimeout 函数时,您可以看到数组排序,当它在函数中时不起作用。该项目已经安装了 bootstap@next 和 popper.js

import React, { useEffect, useState, Fragment } from 'react';
import 'bootstrap/dist/css/bootstrap.css';
import 'bootstrap/dist/js/bootstrap.js';
import './App.css'

function App() {
    const [values, setValues] = useState([43,4,3,56,6,3,36,56,7,5,45,34,87,99,34]);
    const [render, setRender] = useState(false)

    function forceRender() {
        setRender(true)
    }

    useEffect(() => {
        if(render) {
            setRender(false)
        }
    }, [render])

    function bubble(arr) {
        for(let j = 0, len = arr.length; j < len; j++) {
            for (let i = 0; i < len; i++) {
                if (arr[i] > arr[i + 1]) {
                    setTimeout(()=>{
                        let temp = arr[i + 1];
                        arr[i + 1] = arr[i];
                        arr[i] = temp;
                        setValues(arr);  
                    }, 50)       
                }
            }
        }
    }

    useEffect(() => {
        bubble(values)
    }, []);

    useEffect(() => {
        forceRender()
    }, [values])

    return (
        <Fragment>
            <nav className="navbar navbar-dark bg-dark">
                <div className="container">
                    <span className="navbar-brand mb-0 h1">React Sorting</span>
                </div>
            </nav>
            <div className="container sorting-container">
                <div className="row">
                    <div className="sorting">
                        {
                            values.map(e => {
                                return (
                                    <div className="bar" style={{height: `${e}%`}}>
                                        <div className="bar-text">{e}</div>
                                    </div>
                                )
                            })
                        }
                    </div>
                </div>
            </div>
        </Fragment>
    );
}

export default App;

标签: javascriptreactjsasynchronous

解决方案


这段代码有很多问题阻止它工作。仅举几例:

  • forceRender()功能不是必需的,也不起作用。
  • useEffect正在打破一些 React hooks 规则。该功能取决于两者bubble(),并且values- 任何具有像 ESLint 这样的 linter 的好的 IDE 都会指出这个问题并帮助指导您。作为一种快速的解决方法并且为简单起见,我刚刚移动了bubble()内部,以及安装时初始数组的设置,以避免需要获取values.
  • for循环bubble()运行到完成,所有setTimeout回调都指向同一个实例,arr该实例已经运行到完成(这是经典 JS 面试问题的变体)。这就是为什么它从不重新渲染的原因——因为 in 中的值arr已经运行到它们的最终状态并且没有改变。为了在不同时间点捕获数组的状态而不对原始代码进行重大更改,您可以在块范围内制作副本(请参见下面的代码)。
  • setTimeout毫秒都设置为同一时间,因此它们将同时运行。我怀疑您想查看逐个播放的进度,在这种情况下,您可以使时间成为循环ji的函数-例如for50 * i * j
  • JSX map 函数中返回的元素应该有唯一的键。这是您应该在控制台中看到的运行时错误。
  • 为了简单起见,我还减少了数组中的重复项,因此 React 更容易跟踪子元素的唯一键。

您可以运行的工作代码片段,以供参考:

const { useEffect, useState, Fragment } = React;

function App() {
  const [values, setValues] = useState([]);

  useEffect(() => {
function bubble(arr) {
  // Note: this loop runs to completion before any setTimeout callbacks are run.
  for (let j = 0, len = arr.length - 1; j < len; j++) {
    for (let i = 0; i < len; i++) {
      if (arr[i] > arr[i + 1]) {
        // Make a copy so the setTimeout below has a "snapshot" of the array at
        // this point in time.
        const arrCopy = [...arr];

        let temp = arrCopy[i + 1];
        arrCopy[i + 1] = arrCopy[i];
        arrCopy[i] = temp;

        arr = arrCopy;

        setTimeout(() => {
          setValues(arrCopy);
        }, 100 * i * j);
      }
    }
  }
}
bubble([43, 4, 3, 56, 6, 36, 7, 5, 45, 34, 87, 99]);
  }, []);

  return (
<Fragment>
  <nav className="navbar navbar-dark bg-dark">
    <div className="container">
      <span className="navbar-brand mb-0 h1"> React Sorting </span>
    </div>
  </nav>
  <div className="container sorting-container">
    <div className="row">
      <div className="sorting">
        {values.map((val) => {
          return (
            <div key={val} className="bar">
              <div className="bar-text"> {val} </div>
            </div>
          );
        })}
      </div>
    </div>
  </div>
</Fragment>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<body>
  <div id="root" />
</body>


推荐阅读