在有地图的实际业务中会有一个经常的场景,那就是地图的初始化,一般如下:
useEffect(() => {
initMap();
}, [visible]);
我们经常利用Modal组件中open属性状态true与false来作为监听地图初始化的条件。
<Modal
title={<div className="title">新增设备</div>}
width={1120}
className="argicul-modal"
footer={null}
centered
closable={true}
onCancel={onCancel}
open={visible}>
......
</Modal>
对于一般的业务处理来说,通过监听一个状态决定地图的实例化是没问题的。
bug问题:
如果需要在Modal组件上这个地图上点线面要素的添加与删除、点线面要素的绘制交互与清除等操作时,会出现一个bug:就是每次关闭Modal组件后再打开,会发现再进行添加或删除要素失效,清除再一次绘制完的要素也会失效。
1.如:第一次打开Modal时,添加与删除这个点要素,正常实现。当关闭组件再次打开操作时,发现删除无效,添加点要素后位置也没更新。
bug原因:
一般我们可能首先回去寻找要素绘制与删除的逻辑是否存在问题,花费大量时间发现没错。
出现这个bug最主要的原因就是每一次监听map实例化的条件变一次,map就会再实例化一次,简而言之就是每次关闭再打开的这个操作会导致map多次实例化,Modal组件上会渲染多层map地图,我们第一次对要素的各种操作其实是在第一个实例化的map上,也就是视图上最底下那层。因此,我们对于要素的各种操作实际上生效了,只是被后面实例化的map图层掩盖了,因而表现出失效的假象。
bug解决方法:
既然我们弄清楚了是map实例化多次导致的,那么接下来就是想办法让**map在一开始只实例化一次。**所以需要一个在此页面展示时只变化一次的一个状态值。一般点击到Modal组件的父组件这各页面时,找到此时让map实例化一次的条件。
一般两种情况:
1.Modal组件的父组件的props传值参数中是否有唯一变化的 “标识” ,比如有多个tabs组件,Modal就在其中一个tabs组件中,看看每个tabs组件从其父组件上传过来的props中是否有区别tabs的 “标识”。(如果实在没有再看看父组件中有办法传一个“标识”给子组件的props不)
比如说此时我有如下tabs组件,其中每个tab的props中有个属性不同:
那么监听的条件找到了,拿到这个值,监听这个值,在这里还会有一个bug,如果直接监听这个值,会出现以下bug:
Too many re-renders. React limits the number of renders to prevent an infinite loop.
原因在这篇博客中:https://blog.csdn.net/fish_skyyyy/article/details/119137987
解决办法:在Modal打开的点击事件中进行值的判断与状态的设置,然后在地图上监听。
const [mapvisible, setmapvisible] = useState(false);
//打开Modal页面的按钮事件
const addDevice = () => {
setVisible(true);
if(props.title.props.children=='农情设备'){
setmapvisible(true)
}
};
useEffect(() => {
initMap();
}, [mapvisible]);
2.一般在“链式组件”的使用场景中(点击组件A弹出B,点击B弹出C)
比如在B组件中:
const SelectDialog = forwardRef((props, ref) => {
const [selectedItem, setSelectedItem] = useState("rtk");
const [visible, setVisible] = useState(false);
const [mapvisible, setmapvisible] = useState(false);
const rtkLandRef = useRef<any>(null);
//使用useImperativeHandle进行上下传递方法
useImperativeHandle(ref, () => ({
setVisible,
setmapvisible
}));
const selectItem = () => {
setVisible(false);
if (selectedItem == "rtk") {
//组件C(RTKLand)可以使用这两个方法,且B组件的状态值也可带入
rtkLandRef.current?.setVisible(true);
rtkLandRef.current?.setmapvisible(true);
}
}
const select = (type: string) => {
setSelectedItem(type);
}
return (
<div>
<Modal open={visible} width={680} closable={false} onCancel={onCancel} centered forceRender={true} footer={false} bodyStyle={{ textAlign: "center", padding: 48 }}>
<div className={`Land-item ${selectedItem == 'rtk' ? 'Land-item-selected' : ''}`} onClick={() => { select('rtk') }}>打点仪数据录入</div>
</Modal>
<RTKLand ref={rtkLandRef}></RTKLand>
</div>
)
}
这样C组件(RTKLand)就可以拿到mapvisible的状态值进行map实例化监听,点击B组件时才会改变一次mapvisible的状态值,在C组件中再无论打开还是关闭C组件中的Modal组件就不会多次渲染map了。
const RTKLand = forwardRef((props, ref) => {
const [visible, setVisible] = useState(false);
const [mapvisible, setmapvisible] = useState(false);
useImperativeHandle(ref, () => ({
setVisible,
setmapvisible
}));
useEffect(() => {
initMap();
}, [mapvisible]);
return (
<Modal
title={<div className="rtk-title">{title}</div>}
open={visible}
width={1120}
onCancel={onCancel}
footer={null}
centered>
......
</Modal>
}