我正在尝试制作一个算法可视化应用程序,现在正在研究插入排序。它以一个数据数组和一个 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 元素,并显示正在排序的数组。这让我相信状态必须在更新,而不是重新渲染?
答案 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
});