很长,但你会明白为什么会发生这种情况以及更好的修复。闭包尤其是一个问题(也很难理解),主要是当我们设置依赖于 state 的点击处理程序时,如果具有新作用域的处理程序函数没有重新附加到点击事件,那么闭包将保持未更新和因此,陈旧状态保留在点击处理函数中。
如果您在组件中完全理解它,useCallback则会返回对更新函数的新引用,即在其范围内onMapClick 具有更新的标记(状态),但是因为您仅在安装组件时才在开始时设置“单击”处理程序,点击处理程序保持未更新,因为您已经放置了一个 check if(! map.current),这会阻止任何新的处理程序附加到地图上。
// in sandbox map.js line 40
useEffect(() => {
// this is the issue, only true when component is initialized
if (! map.current) {
map.current = Leaflet.map("mapid4").setView([46.378333, 13.836667], 12);
Leaflet.tileLayer({ ....}).addTo(map.current);
// we must update this since onMapClick was updated
// but you're preventing this from happening using the if statement
map.current.on("click", onMapClick);
}
}, [onMapClick]);
现在我尝试map.current.on("click", onMapClick);移出if块,但是有一个问题,Leaflets 不是用新函数替换点击处理程序,而是添加了另一个事件处理程序(基本上是堆叠事件处理程序),所以我们必须在添加新的之前删除旧的,否则我们最终会在每次onMapClick更新时添加多个处理程序。我们有off()函数。
这是更新后的代码
// in sandbox map.js line 40
useEffect(() => {
// this is the issue, only true when component is initialized
if (!map.current) {
map.current = Leaflet.map("mapid4").setView([46.378333, 13.836667], 12);
Leaflet.tileLayer({ ....
}).addTo(map.current);
}
// remove out of the condition block
// remove any stale click handlers and add the updated onMapClick handler
map.current.off('click').on("click", onMapClick);
}, [onMapClick]);
这是更新后的沙箱的链接,它运行良好。
现在有另一个想法来解决它,而无需每次都更换点击处理程序。即一些全局变量,我相信这还不算太糟糕。
为此globalMarkers,在组件之外但在组件上方添加并每次更新它。
let updatedMarkers = [];
const Map4 = () => {
let map = useRef(null);
let path = useRef({});
updatedMarkers = markers; // update this variable each and every time with the new markers value
......
const onMapClick = useCallback((event) => {
console.log('onMapClick markers', markers)
const marker = Leaflet.marker(event.latlng, {
draggable: true,
icon: Leaflet.divIcon({
// use updatedMarkers here
html: updatedMarkers.length + 1,
className: 'marker-text',
}),
}).addTo(map.current).on('move', onMarkerMove)
setMarkers((existingMarkers) => [ ...existingMarkers, marker])
}, [markers, onMarkerMove])
.....
} // component end
这个也很完美,用这个代码链接到沙箱。这个工作得更快。
最后,上面将它作为参数传递的解决方案也可以!我更喜欢带有更新if块的那个,因为它很容易修改,而且你可以理解它背后的逻辑。