我正在尝试使用 React 上的 Mapbox 来控制图层上的事件监听器。 map.off 应该可以做到这一点,但它并没有删除图层中的 onclick 事件。 https://docs.mapbox.com/mapbox-gl-js/api/map/#map#off
但我做错了什么,所以我无法删除该事件。 这是我到目前为止所做的...
要添加事件,我这样做
map.on('click', 'building_footprints_click', addBuildingPopUp)
为了尝试删除它,我尝试过:
map.off('click', 'building_footprints_click', addBuildingPopUp);
map.off('click', 'building_footprints_click');
map.off('click', addBuildingPopUp);
但它们都不起作用。 我读到我必须发送 on 事件的实例。所以我尝试:
let event = map.on('click', 'building_footprints_click', addBuildingPopUp)
以及与上面相同的三个关闭操作,但它们也不起作用
map.off('click', 'building_footprints_click', event);
map.off('click', 'building_footprints_click');
map.off('click', event);
还有监听器功能, 我尝试过:
const addBuildingPopUp = (e) => {}
and
function addBuildingPopUp (e) {}
and
let addBuildingPopUp = function building (e) {}
这是一个基本的 Stackblitz,其中包含非工作函数的示例 https://stackblitz.com/edit/react-5maykf?file=src/App.js
根据您的 stackblitz 我可以看到问题是您正在尝试取消注册不再存在的函数。
对于mapbox,您需要注册和注销相同的功能。我是说
map.on('mousemove', f1)
和 map.off('mousemove', f1)
您需要确保 f1 保持不变。
让我解释一下,每当 React 组件渲染时,它都会重新创建其体内的所有变量,除非它是状态变量的一部分,因为函数
addBuildingPopUp
在每个渲染上创建,mapboxgl 不会取消注册事件。
您需要做的唯一更改是确保
addBuildingPopUp
保持不变,您应该在 React 组件之外定义。
我检查了您的 stackblitz 代码,并用注释相应地更新了工作答案。如果您对此有任何疑问,请告诉我
https://stackblitz.com/edit/react-mqjjkg?devtoolsheight=33&file=src/App.js
传递给mapbox事件监听器的函数必须完全相同。每次在 React 中渲染组件时,该函数(尽管保持不变)都会被 javascript 识别为新声明的函数。因此,mapbox 将“关闭”一个不存在事件的监听器,因为它是一个从未存在过的函数。为了解决这个问题,你必须使用React“useCallback”。它确保功能在组件存在的过程中保持不变
export default function MapListeners() {
const mode = useRecoilValue(interactivityMode);
const setModal = useSetRecoilState(modalState);
const removeListeners = () => {
cuMap.off("click", setAddBeaconModal);
};
const setAddBeaconModal = useCallback(
// useCallback ensures the functions stays identical
(e: mapboxgl.MapMouseEvent & mapboxgl.EventData) => {
// const coordinates = e.lngLat;
setModal({
key: AddBeaconModal.toString(),
body: {
component: AddBeaconModal,
props: { word: "irgendwas", title: "Irrr" },
},
title: "Add Beacon",
});
},
[]
);
const setMapListeners = () => {
removeListeners();
switch (mode) {
case "add":
cuMap.on("click", setAddBeaconModal);
break;
case "lock":
break;
default:
}
};
useEffect(setMapListeners, [mode]);
return null;
}
我已经在 React 中完成了。我花了几天时间尝试这样做。因此,就我而言,我想删除一个单击侦听器,该侦听器将添加一些自定义标记。这是我的做法(用于测试):
Attributes in File
const map = useRef(null);
map.current.on('click', (event) => addMarkerOnClick(event));
setTimeout(() => {
console.log("Disabled")
map.current.off('click', (event) => addMarkerOnClick(event));
}, 5000)
上面是我最初的代码,它不起作用。
map.current.on('click', addMarkerOnClick);
setTimeout(() => {
console.log("Disabled")
map.current.off('click', addMarkerOnClick);
}, 5000)
这就是解决方案^
对于没有 React 用户,我建议:
// Attributes
let map = null
map.on('click', addMarkerOnClick);
setTimeout(() => {
console.log("Disabled")
map.off('click', addMarkerOnClick);
}, 5000)
如果您使用 React,只需将处理程序存储在 useRef 中,瞧,一切都会正常工作,这是类似的情况和建议的解决方案
这是解决方案的代码:
const handleClickRef = useRef(handleClick)
handleClickRef.current = handleClick
map.on('mousedown', 'layer-id', handleClickRef.current)
map.off('mousedown', 'layer-id', handleClickRef.current)
我会帮忙的,因为今天早上我也遇到了类似的问题。我最终采用的解决方案是将传递给 map.on 和 map.off 的函数存储在带有空括号的 useCallback 挂钩内。效果非常好!
在 React 上,您需要在组件外部定义函数
const showLayerInfo = (e: MapMouseEvent & { features?: MapboxGeoJSONFeature[] | undefined; } & EventData) => {
console.log(e)
}
export default function Map(props: MapProps) {
const mapContainer = useRef<HTMLDivElement>(null)
const map = useRef<mapboxgl.Map>()
map.current.off("mouseenter", layerData.name, showLayerInfo)
map.current.on("mouseenter", layerData.name, showLayerInfo)
return <>
<div
ref={mapContainer}
className="map-container"
style={{width: "100%", height: "100vh"}}
/>
</>
}
我不知道你写这个问题时是否写错了,但你写了
map.on('click', 'building_footprints_click', addBuildingPop**UP**);
map.off('click', 'building_footprints_click', addBuildingPop**Up**);
注意到函数名称中大小写的差异吗?
此语法是正确的,您应该使用它
如果问题是由于拼写错误造成的,我建议使用 linter 来避免将来出现这种情况