React 组件不会随着状态变化而更新

时间:2021-05-28 17:15:48

标签: javascript reactjs asynchronous

我正在尝试制作一个算法可视化应用程序,现在正在研究插入排序。它以一个数据数组和一个 setData() 钩子为参数,每隔几毫秒设置一次状态(让用户看到渐变的排序效果):

const delay = (ms) => new Promise((r) => setTimeout(r, ms)) // helper func

async function insertionSort(data, setData) {
  const inputArr = [...data]
  let n = inputArr.length
  for (let i = 1; i < n; i++) {
    await delay(200) // pause
    // Choosing the first element in our unsorted subarray
    let current = inputArr[i]
    // The last element of our sorted subarray
    let j = i - 1
    while (j > -1 && current < inputArr[j]) {
      await delay(200) // pause
      inputArr[j + 1] = inputArr[j]
      j--
    }
    inputArr[j + 1] = current
    setData(inputArr)
  }
  setData(inputArr)
}

export default insertionSort

在主应用程序中调用排序函数,并将数据映射到 DOM 元素,如下所示:

const [data, setData] = useState([72,22,131,30,402]) //array to be sorted

return (
    <div className="App">
      <button
        onClick={async () => {
          await insertionSort(data, setData)
        }}>
        Insertion Sort
      </button>



data.map((val, i) => (
            <div key={`${val}${i}`}>{val}</div>
          ))
    </div>
)

问题是 DOM 元素不会在每次 setData() 调用时重新渲染。 如果我注释掉 await 命令,排序功能将正常工作。其他时候我遇到这个问题通常是因为给定的键不是 DOM 元素独有的,但我非常确定它们在这种情况下是。

强制单独的状态更新(比如通过另一个按钮向数组添加一个数字)确实会刷新 dom 元素,并显示正在排序的数组。这让我相信状态必须在更新,而不是重新渲染?

2 个答案:

答案 0 :(得分:2)

它与事件处理程序中的反应批处理状态更新有关

请检查此link

我试图通过将 sort 更改为生成器函数和带有 useEffect 钩子的迭代器模式来解决它。 -> My Solution

它不是一个完美的解决方案。但也许它可以帮助您提出一些想法:)

如果您无法访问链接,这里是代码

App.js

import sort from "./sort";
import "./styles.css";
import { useEffect, useState, useRef } from "react";

const delay = (ms) => new Promise((r) => setTimeout(r, ms)); // helper func

const initialArray = [72, 22, 402, 131, 30];

const initialData = { data: initialArray, sortStep: -1 };

export default function App() {
  const [state, setState] = useState(() => initialData);
  const iterator = useRef();

  const runSort = async () => {
    if (state.sortStep >= 0) {
      const { value, done } = iterator.current.next();
      if (!done) { // Here since sort is not done.We go to next sortStep
        await delay(1000);
        setState((s) => ({ ...s, data: value, sortStep: s.sortStep + 1 }));
      } else {
        // Sorting is done. Maybe reset things up.
      }
    }
  };

  const startSort = () => {
    iterator.current = sort(initialArray);
    setState((s) => ({ ...s, data: initialArray, sortStep: 0 }));
    // Here we initiate sort by setting the sortStep to zero
  };

  useEffect(() => {
    runSort();
  }, [state.sortStep]); // Whenever ${sortStep} changes we try to sort again it to next step untill the sorting is complete

  return (
    <div className="App">
      
      <button onClick={startSort}>Insertion Sort</button>

      {state.sortStep >= 0 && <h4>Step: {state.sortStep}</h4>}

      {state.data.map((val, i) => (
        <div key={`${val}${i}`}>{val}</div>
      ))}
    </div>
  );
}

sort.js

function* sort(data) {
  const inputArr = [...data];
  let n = inputArr.length;
  for (let i = 1; i < n; i++) {
    // Choosing the first element in our unsorted subarray
    let current = inputArr[i];
    // The last element of our sorted subarray
    let j = i - 1;
    while (j > -1 && current < inputArr[j]) {
      inputArr[j + 1] = inputArr[j];
      j--;
    }
    inputArr[j + 1] = current;
    yield inputArr;
  }
}

export default sort;

答案 1 :(得分:0)

当你根据之前的状态设置状态时使用匿名函数

setData((state, props) => {
   //Do something here 
});