我一直在React中构建一个迷宫实现。基本逻辑是,每次我运行go函数,通过Array.from()将useState设置为一个新的100 * 100数组时,它就会检查其邻居是否可用。
这是我的迷宫外观
const App: React.FC = () => {
const Maze: MazeConstructor = MazeData;
const maze: IMaze = new Maze(mazeJson);
const [data, setData] = useState(maze.maze);
const handleClick = (): void => {
go(maze.entranceX, maze.entranceY);
}
const go = (x: number, y: number): void => {
let timer;
if (!maze.isInArea(x, y)) throw new Error('x or y is not inside the maze')
maze.visited![x][y] = true;
maze.path[x][y] = true;
maze.maze[x][y] = 1;
setData(Array.from(maze.maze))
if (x === maze.exitX && y === maze.exitY) return;
for (let i = 0; i < 4; i++) {
let newX = x + direction[i][0];
let newY = y + direction[i][1];
if (maze.isInArea(newX, newY) && maze.getCell(newX, newY) === MazeData.ROAD && !maze.visited![newX][newY]) {
setTimeout(()=>go(newX, newY),50);
}
}
if(timer) clearTimeout(timer);
}
return (
<div className="container">
<div className="wrapper">
{
data.map((item, index) =>
// to remove the gap between divs
<div style={{ fontSize: 0 }}>{
item.map(i => {
return <div className={classNames('cell', {
'wall': i === '#',
'path': i === 1,
'road': i === ' ',
})} />
}
)
}</div>
)
}
</div>
<button onClick={handleClick}>Solve</button>
</div>
);
}
为了一次只渲染一次路径,我添加了setTimeout使其起作用。否则,将在不显示每个步骤的情况下呈现路径。但是,我发现完成整个渲染需要永远的时间。我只是想知道是否是导致问题或其他问题的setTimeout。如果是这样,是否有任何其他方法可以解决该问题,从而延迟了递归。
答案 0 :(得分:0)
通过添加setTimeout
,您确实改变了算法。代替了最初的深度优先搜索,它已经变成了广度优先搜索。
遍历邻居时,您会调用多个setTimeout
调用,这些调用将并行挂起。
由于使用go
安排了setTimeout
的调用,而没有对该单元格首先将visited
设置为true,因此您将遇到go
被安排在其上运行的情况相同会多次协调。
还要注意,您永远不会为timer
分配一个值,从而使clearTimeout
不使用。该计划也是不可能的,因为您正在同时计划多个超时。因此,即使在找到目标之后,许多计时器仍将继续超时并仍然调用go
。
对于引入暂停,我建议将go
转换为async
函数,并对基于await
的Promise进行setTimeout
。您可能需要远离递归,而应实施一个迭代解决方案,该解决方案可能基于堆栈(因此搜索顺序仍为DFS)。
这是看起来如何:
// Make function async:
const go = async (x: number, y: number): void => {
if (!maze.isInArea(x, y)) throw new Error('x or y is not inside the maze')
let stack = [[x, y]]; // A stack to replace the use of recursion
while (stack.length) {
let [x, y] = stack.pop();
if (maze.visited![x][y]) continue; // Already visited
maze.visited![x][y] = true;
maze.path[x][y] = true;
maze.maze[x][y] = 1;
setData(Array.from(maze.maze));
await new Promise(resolve => setTimeout(resolve, 10)); // Await 10ms
if (x === maze.exitX && y === maze.exitY) return;
// Reverse the direction of this loop to get the original DFS order
for (let i = 3; i >= 0; i--) {
let newX = x + direction[i][0];
let newY = y + direction[i][1];
if (maze.isInArea(newX, newY) && maze.getCell(newX, newY) === MazeData.ROAD
&& !maze.visited![newX][newY]) {
stack.push([newX, newY]);
}
}
}
}
作为setTimeout
的替代方法,您还可以使用:
await new Promise(requestAnimationFrame); // Await next paint
在每个步骤中渲染上千个div
元素非常困难。完全重新设计后,每个单元都有一个单独的组件,每个单元都有自己的呈现和状态,将大大提高性能。