每次状态更改时都不会渲染 React 组件

时间:2021-04-14 16:05:39

标签: javascript reactjs

我正在尝试使用冒泡排序对数组进行排序,并且在排序的每个步骤中,我都希望呈现数组内值位置的变化。 所以用 React 做了下面的算法,第一次它确实渲染了变化,但后来停止了,不渲染任何东西。我试图控制台记录我正在渲染的数组,我正在控制台上获取每个排序步骤,但不知道为什么它在这里不起作用。 谁能告诉我为什么会这样?还有什么可以做的?

注意:它最初工作正常并呈现排序数组,但后来当我添加 setTimeOut 函数时它不起作用。

App.js

import React, { useState } from "react";

const App = () => {
  const [arrayForSort, setArrayForSort] = useState([44,2,46,11,15,34,1,7,55]);

  const fetchArray = arrayForSort.map((element) => {
    return <span>{element} </span>;
  });

  const bubbleSort = (array) => {
    let newArray = [...array];
    let n = newArray.length;

    for (let i = 0; i < n; i++) {
      for (let j = 0; j < n - i; j++) {
        const myVar = setTimeout(() => {
          if (newArray[j] > newArray[j + 1]) {
            [newArray[j], newArray[j + 1]] = [newArray[j + 1], newArray[j]];
          }
          console.log(newArray);   // render each step of sort 
          setArrayForSort(newArray);   // on screen it just shows "2 44 46 11 15 34 1 7 55" the very first step of sort
        }, 2000);
      }
    }
  };

  return (
    <div>
      <h4>Array: </h4>
      <span>{fetchArray}</span>
      <div>
        <button
          onClick={() => {
            bubbleSort(arrayForSort); 
          }}
        >
          Sort
        </button>
      </div>
    </div>
  );
};

export default App;

1 个答案:

答案 0 :(得分:1)

您的代码几乎没有问题。 2000 是一个常量值,这意味着您的所有 setTimeout 会在 2 秒后一起触发。这就是为什么您的所有 console.logs 同时出现的原因。

其次,setTimeout 将保留 setArrayForSort 的引用。但是每次渲染后都需要一个新的参考。如果您使用类组件,但使用功能组件,您需要以不同的方式思考,这可能会起作用。

这是一个工作演示codesandbox link

import React, { useEffect, useRef, useState } from "react";

const App = () => {
  const [arrayForSort, setArrayForSort] = useState([44, 2, 46, 11, 15, 34, 1, 7, 55]);

  const ijRef = useRef({ i: 0, j: 0 });
  const [isSorting, setIsSorting] = useState(false);

  const fetchArray = arrayForSort.map((element) => {
    return <span key={element}>{element} </span>;
  });

  useEffect(() => {
    if (!isSorting) return;

    const ij = ijRef.current;
    const i = ij.i;
    const j = ij.j;
    const arr = [...arrayForSort];
    const n = arr.length;
    let isSwapped = false;

    if (arr[j] > arr[j + 1]) {
      [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
      isSwapped = true;
    }

    if (j < n - i) {
      ij.j++;
    } else if (ij.i < n) {
      ij.j = 0;
      ij.i++;
    }

    // This will trigger the next render
    // and loop will continue until ij.i < n
    setTimeout(
      () => {
        if (ij.i >= n) {
          setIsSorting(false);
        }
        setArrayForSort(arr);
      },
      isSwapped ? 100 : 0 // delay 100ms for a successful swap
    );
  }, [ijRef, arrayForSort, setArrayForSort, isSorting]);

  const beginSort = () => {
    ijRef.current.i = 0;
    ijRef.current.j = 0;
    setIsSorting(true); // begin sorting
  };

  return (
    <div>
      <h4>Array: </h4>
      <span>{fetchArray}</span>
      <div>
        <button onClick={beginSort}>Sort</button>
      </div>
    </div>
  );
};

export default App;