路径寻路应用递归函数条件终止失效问题求助
问题分析
你的递归停止逻辑失效主要有两个核心原因:
- React useState的闭包陷阱:
stopVisiting是组件状态,startVisiting函数在每次调用时都会捕获当前的stopVisiting值。当你调用setStopVisiting(true)后,已经处于调用栈或setTimeout队列中的startVisiting实例,拿到的还是更新前的false值,不会立即停止。 - setTimeout的任务队列特性:一旦
setTimeout把递归调用排进任务队列,即使后续stopVisiting变为true,这些已经排队的调用还是会执行,除非在执行前再次检查停止条件。
另外代码里还有两个小问题:
- 网格是30行(i从0到29),但判断
visElement.i < 39明显错误,应该改为visElement.i < 29; setGrid是异步更新,setTimeout里直接用grid获取邻居,拿到的可能是旧的网格状态。
解决方案
改用useRef来存储停止标记(因为ref的current属性是可变的,所有函数都能拿到最新值),同时在每次递归调用前检查停止状态,修复网格引用问题:
import { useState, useRef } from 'react'; // ... 其他组件代码 const stopVisitingRef = useRef(false); const [grid, setGrid] = useState(/* 你的初始网格 */); const startVisiting = (visElement) => { // 先检查停止标记,直接终止 if (stopVisitingRef.current) { console.log("Stop the function here"); return; } // 到达终点,设置停止标记 if (visElement.i === endElement.i && visElement.j === endElement.j) { stopVisitingRef.current = true; return; } // 遇到障碍,直接返回 if (visElement.wall) return; // 已访问过,直接返回 if (visElement.visited) return; // 更新网格标记为已访问 const newGrid = [...grid]; newGrid[visElement.i][visElement.j].visited = true; setGrid(newGrid); // 用更新后的newGrid获取邻居,避免旧引用问题 setTimeout(() => { // 每次递归前再次检查停止标记 if (!stopVisitingRef.current && visElement.i > 0) { startVisiting(newGrid[visElement.i - 1][visElement.j]); } if (!stopVisitingRef.current && visElement.i < 29) { startVisiting(newGrid[visElement.i + 1][visElement.j]); } if (!stopVisitingRef.current && visElement.j > 0) { startVisiting(newGrid[visElement.i][visElement.j - 1]); } if (!stopVisitingRef.current && visElement.j < 59) { startVisiting(newGrid[visElement.i][visElement.j + 1]); } }, 500); };
关键修改点
- 用
useRef(false)替代useState(false):stopVisitingRef.current可以被直接修改,所有startVisiting实例都能拿到最新值,避免闭包陷阱; - 将停止检查放在函数最开头:只要标记为
true,直接终止当前调用; - 在
setTimeout的每个递归调用前都检查停止标记:阻止已经排队的不必要调用执行; - 用
newGrid获取邻居元素:确保拿到的是最新的已更新网格状态; - 修复了行号判断的错误:30行网格的最大行索引是29。
内容的提问来源于stack exchange,提问作者chaos
相关产品推荐
相关产品推荐

