react-Leaflet中Popup尺寸不随内容变化大小问题

通过 react-leaflet 在 react 中使用 leaflet,在动态修改 Popup 弹窗内容时,发现 Popup 尺寸(宽高)不会跟随修改后的内容变化。
问题
有一个默认小尺寸的 Popup,它里面有一个按钮。点击按钮后会将 Popup 中的内容替换成大尺寸内容。
简要逻辑如下:
<Popup>
{this.state.fullBtnClicked ? bigContent : smallContent}
</Popup>
替换内容后,发现 Popup 的尺寸并未变大。
尝试添加minWidth
/maxWidth
属性:
<Popup
maxWidth={this.state.fullBtnClicked? bigMaxSize: smallMaxSize}
minWidth={this.state.fullBtnClicked? bigMinSize : smallMinSize}
>
{this.state.fullBtnClicked ? bigContent : smallContent}
</Popup>
替换内容后,Popup 的尺寸仍未变大,且 leaflet 仍然沿用初始化时的minWidth
/maxWidth
。
原因
leaflet 会根据计算值为 Popup Content 添加一个width = xxx
样式(Height 同理但稍有不同)。
计算方法在 leaflet/Popup.js 的 _updateLayout
中:
style.width = '';
style.whiteSpace = 'nowrap';
var width = container.offsetWidth;
width = Math.min(width, this.options.maxWidth);
width = Math.max(width, this.options.minWidth);
style.width = (width + 1) + 'px';
style.whiteSpace = '';
style.height = '';
var height = container.offsetHeight,
maxHeight = this.options.maxHeight,
scrolledClass = 'leaflet-popup-scrolled';
if (maxHeight && height > maxHeight) {
style.height = maxHeight + 'px';
DomUtil.addClass(container, scrolledClass);
} else {
DomUtil.removeClass(container, scrolledClass);
}
其中最关键的便是 this.options 了。
一路追查,发现 options 的路径为:
- [@react-leaflet]SomeWork
- [react-leaflet]createDivOverlayComponent.createOverlayComponent
- [leaflet]new Popup(props,ref)
- [leaflet]popup.initialize(props,ref)
- [leaflet]options = props
- [leaflet]popup.initialize(props,ref)
- [leaflet]new Popup(props,ref)
- [react-leaflet]createDivOverlayComponent.createOverlayComponent
问题明确:
- leaflet 这个包是传统 js,不是 React 组件。
- react-leaflet 给 leaflet 套了个 React 空壳,壳里的内容实际为 leaflet 和它的 Dom。
- props 发生变化时,react-leaflet 仅针对 position 做了监听更新,未处理 className、minWidth 等属性的更新。
当然,我不是专业的 Web 前端。
解决方法
方法0:使用我修改好的 react-leaflet
将package.json
中的依赖修改成我传的包:
"dependencies":{
"react-leaflet": "npm:@azimiao/react-leaflet"
}
方法1:改 react-leaflet
修改 Popup.tsx,使其监听minWidth
/maxWidth
变化来更新 leaflet 中的 options 值,并重新计算 layout:
function usePopupLifecycle(
element: LeafletElement<LeafletPopup>,
context: LeafletContextInterface,
// 梓喵出没(azimiao.com)修改 1,添加了 width/height
{ position, minWidth, maxWidth, maxHeight },
setOpen: SetOpenFunc,
) {
// 前面代码略……
// 梓喵出没(azimiao.com)修改 2,监听变化
const firstUpdate = useRef(true)
useLayoutEffect(() => {
if (firstUpdate.current) {
firstUpdate.current = false
return
}
const { instance } = element
if (minWidth != null) {
instance.options.minWidth = minWidth
}
if (maxWidth != null) {
instance.options.maxWidth = maxWidth
}
if (maxHeight != null) {
instance.options.maxHeight = maxHeight
}
instance.update()
}, [element, minWidth, maxWidth, maxHeight])
}
方法2:Bug 就是 feature
通过 props 给minWidth
传个 string 类型的值(如auto
), Popup 计算 layout 时会出现错误结果(NaN),其赋值的 style 将没有意义。
通过 CSS 限制 content 内容尺寸,控制窗口大小。
方法3:改 leaflet
修改 _updateLayout
方法,在minWidth
/maxWidth
为 string 或为 null 情况下,不设置 style,默认被 child 撑开即可。
同样通过 CSS 限制 content 内容尺寸来控制窗口大小。
我虽然不是专业前端,但我建议使用方法 1。