从反应传单中的地图中删除缩放控件
Remove Zoom control from map in react-leaflet
我正在构建一个 react-leaflet 应用程序,我正在尝试将缩放控件与地图本身分开。此处询问了香草 Leaflet 上下文中的相同问题:Placing controls outside map container with Leaflet?。这就是我试图在 react-leaflet 框架内完成的工作。这是我的项目的大纲:
import UIWindow from './UIWindow'
import Map from './MapRL'
class App extends React.Component {
render () {
return (
<div className="App">
<Map />
<UIWindow />
</div>
)
}
}
export default App;
我的地图组件如下所示:
import React from 'react'
import { Map as LeafletMap, TileLayer } from 'react-leaflet'
class Map extends React.Component {
render () {
return(
<LeafletMap
className="sidebar-map"
center={...}
zoom={...}
zoomControl={false}
id="mapId" >
<TileLayer
url="..."
attribution="...
/>
</LeafletMap>
)
}
}
export default Map;
然后我的 UIWindow 看起来像这样:
class UIWindow extends React.Component {
render () {
return(
<div className="UIWindow">
<Header />
<ControlLayer />
</div>
)
}
}
最后,我的 ControlLayer(我希望 ZoomControl 所在的位置)应该如下所示:
class ControlLayer extends React.Component {
render () {
return (
<div className="ControlLayer">
<LeftSidebar />
<ZoomControl />
{this.props.infoPage && <InfoPage />}
</div>
)
}
}
当然,使用当前代码,将 ZoomControl 放在 ControlLayer 中会引发错误:TypeError: Cannot read 属性 '_zoom' of undefined,有一些更详细的写出问题所在,以及有关缩放功能的 Leaflet 内部代码的所有参考。
(DomUtil.removeClass(this._zoomInButton, className);
,等等)
我预计会出现错误,因为 ZoomControl 不再是 <Map />
组件的子组件,而是 <Map />
兄弟组件的孙组件。我知道 react-leaflet 在其上下文提供者和消费者 LeafletProvider 和 LeafletConsumer 上的功能。当我尝试从我的 <ControlLayer />
中调用我的 LeafletConsumer 时,我什么也没得到。例如:
<LeafletConsumer>
{context => console.log(context)}
</LeafletConsumer>
这记录了一个空对象。很明显,我的 ControlLayer 中的 LeafletConsumer 没有正确连接到 <Map />
中的 LeaflerProvider。我是否需要使用 LeafletProvider 以某种方式从 Map 导出上下文?我对 React Context API 有点陌生,所以这对我来说还不是很直观。 (应用程序的其余大部分将使用 React Redux 来管理组件之间的状态更改和通信。这就是我计划将侧边栏的内容连接到地图本身的方式。我的侧边栏似乎没有任何问题与 <Map />
).
完全断开
如何正确地将此 ZoomControl 连接到我的地图组件?
更新:
我尝试在我的 redux 存储中捕获上下文,然后将其提供给我的外部化 ZoomControl。像这样:
<LeafletConsumer>
{ context => {
this.props.captureContext(context)
}}
</LeafletConsumer>
这将捕获上下文作为我的 redux 存储的一部分。然后我将其用作新上下文中的值:
// ControlLayer.js
const MapContext = React.createContext()
<MapContext.Provider value={this.props.mapContext}>
<LeftSidebar />
<MapContext.Consumer>
{context => {
if (context){
console.log(ZoomControl);
}
}}
</MapContext.Consumer>
</MapContext.Provider>
其中 this.props.mapContext
是从我的 redux matchStateToProps 引入的,它正是 captureContext 函数捕获的上下文。
仍然,这不起作用。我的 React 开发工具显示 MapContent.Consumer 给出的值与当 ZoomControl 在 Map 组件内时 react-leaflet 的固有 '' 给出的值完全相同。但我仍然收到相同的错误消息。在这里很沮丧。
我不确定如何使用你的方法来实现这一点,当你试图将它放在地图包装器之外时,react-leaflet 的包装器 ZoomControl 不是 Map 包装器的子级。
但是,对于像 ZoomControl 这样的小控件,一个简单的解决方案是创建一个自定义的 Zoom 组件,与原始组件相同,使用本机 css 样式轻松构建它,并在访问地图元素后, 分别调用放大和缩小方法。
在下面的示例中,我使用 react-context 在地图加载后保存地图元素:
useEffect(() => {
const map = mapRef.current.leafletElement;
setMap(map);
}, [mapRef, setMap]);
然后在这里使用地图引用制作一个与原生相同的自定义 Zoom 组件(css 参见演示):
const Zoom = () => {
const { map } = useContext(Context);
const zoomIn = e => {
e.preventDefault();
map.setZoom(map.getZoom() + 1);
};
const zoomOut = e => {
e.preventDefault();
map.setZoom(map.getZoom() - 1);
};
return (
<div className="leaflet-bar">
<a
className="leaflet-control-zoom-in"
href="/"
title="Zoom in"
role="button"
aria-label="Zoom in"
onClick={zoomIn}
>
+
</a>
<a
className="leaflet-control-zoom-out"
href="/"
title="Zoom out"
role="button"
aria-label="Zoom out"
onClick={zoomOut}
>
−
</a>
</div>
);
};
然后把它放在你喜欢的地方:
const App = () => {
return (
<Provider>
<div style={{ display: "flex", flexDirection: "row" }}>
<Leaflet />
<Zoom />
</div>
</Provider>
);
};
这里是没有钩子的相同方法:
提供商应如下所示:
class Provider extends Component {
state = { map: null };
setMap = map => {
this.setState({ map });
};
render() {
return (
<Context.Provider value={{ map: this.state.map, setMap: this.setMap }}>
{this.props.children}
</Context.Provider>
);
}
}
传单组件将是:
class Leaflet extends Component {
mapRef = createRef(null);
componentDidMount() {
const map = this.mapRef.current.leafletElement;
this.props.setMap(map);
}
render() {
return (
<Map
style={{ width: "80vw", height: "60vh" }}
ref={this.mapRef}
center={[50.63, 13.047]}
zoom={13}
zoomControl={false}
minZoom={3}
maxZoom={18}
>
<TileLayer
attribution='&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?"
/>
</Map>
);
}
}
现在要访问 compoenntDidMount 的 setMap 函数,您需要执行以下操作:
export default props => (
<Context.Consumer>
{({ setMap }) => <Leaflet {...props} setMap={setMap} />}
</Context.Consumer>
);
其余的请看这里:Demo
我正在构建一个 react-leaflet 应用程序,我正在尝试将缩放控件与地图本身分开。此处询问了香草 Leaflet 上下文中的相同问题:Placing controls outside map container with Leaflet?。这就是我试图在 react-leaflet 框架内完成的工作。这是我的项目的大纲:
import UIWindow from './UIWindow'
import Map from './MapRL'
class App extends React.Component {
render () {
return (
<div className="App">
<Map />
<UIWindow />
</div>
)
}
}
export default App;
我的地图组件如下所示:
import React from 'react'
import { Map as LeafletMap, TileLayer } from 'react-leaflet'
class Map extends React.Component {
render () {
return(
<LeafletMap
className="sidebar-map"
center={...}
zoom={...}
zoomControl={false}
id="mapId" >
<TileLayer
url="..."
attribution="...
/>
</LeafletMap>
)
}
}
export default Map;
然后我的 UIWindow 看起来像这样:
class UIWindow extends React.Component {
render () {
return(
<div className="UIWindow">
<Header />
<ControlLayer />
</div>
)
}
}
最后,我的 ControlLayer(我希望 ZoomControl 所在的位置)应该如下所示:
class ControlLayer extends React.Component {
render () {
return (
<div className="ControlLayer">
<LeftSidebar />
<ZoomControl />
{this.props.infoPage && <InfoPage />}
</div>
)
}
}
当然,使用当前代码,将 ZoomControl 放在 ControlLayer 中会引发错误:TypeError: Cannot read 属性 '_zoom' of undefined,有一些更详细的写出问题所在,以及有关缩放功能的 Leaflet 内部代码的所有参考。
(DomUtil.removeClass(this._zoomInButton, className);
,等等)
我预计会出现错误,因为 ZoomControl 不再是 <Map />
组件的子组件,而是 <Map />
兄弟组件的孙组件。我知道 react-leaflet 在其上下文提供者和消费者 LeafletProvider 和 LeafletConsumer 上的功能。当我尝试从我的 <ControlLayer />
中调用我的 LeafletConsumer 时,我什么也没得到。例如:
<LeafletConsumer>
{context => console.log(context)}
</LeafletConsumer>
这记录了一个空对象。很明显,我的 ControlLayer 中的 LeafletConsumer 没有正确连接到 <Map />
中的 LeaflerProvider。我是否需要使用 LeafletProvider 以某种方式从 Map 导出上下文?我对 React Context API 有点陌生,所以这对我来说还不是很直观。 (应用程序的其余大部分将使用 React Redux 来管理组件之间的状态更改和通信。这就是我计划将侧边栏的内容连接到地图本身的方式。我的侧边栏似乎没有任何问题与 <Map />
).
如何正确地将此 ZoomControl 连接到我的地图组件?
更新:
我尝试在我的 redux 存储中捕获上下文,然后将其提供给我的外部化 ZoomControl。像这样:
<LeafletConsumer>
{ context => {
this.props.captureContext(context)
}}
</LeafletConsumer>
这将捕获上下文作为我的 redux 存储的一部分。然后我将其用作新上下文中的值:
// ControlLayer.js
const MapContext = React.createContext()
<MapContext.Provider value={this.props.mapContext}>
<LeftSidebar />
<MapContext.Consumer>
{context => {
if (context){
console.log(ZoomControl);
}
}}
</MapContext.Consumer>
</MapContext.Provider>
其中 this.props.mapContext
是从我的 redux matchStateToProps 引入的,它正是 captureContext 函数捕获的上下文。
仍然,这不起作用。我的 React 开发工具显示 MapContent.Consumer 给出的值与当 ZoomControl 在 Map 组件内时 react-leaflet 的固有 '' 给出的值完全相同。但我仍然收到相同的错误消息。在这里很沮丧。
我不确定如何使用你的方法来实现这一点,当你试图将它放在地图包装器之外时,react-leaflet 的包装器 ZoomControl 不是 Map 包装器的子级。
但是,对于像 ZoomControl 这样的小控件,一个简单的解决方案是创建一个自定义的 Zoom 组件,与原始组件相同,使用本机 css 样式轻松构建它,并在访问地图元素后, 分别调用放大和缩小方法。
在下面的示例中,我使用 react-context 在地图加载后保存地图元素:
useEffect(() => {
const map = mapRef.current.leafletElement;
setMap(map);
}, [mapRef, setMap]);
然后在这里使用地图引用制作一个与原生相同的自定义 Zoom 组件(css 参见演示):
const Zoom = () => {
const { map } = useContext(Context);
const zoomIn = e => {
e.preventDefault();
map.setZoom(map.getZoom() + 1);
};
const zoomOut = e => {
e.preventDefault();
map.setZoom(map.getZoom() - 1);
};
return (
<div className="leaflet-bar">
<a
className="leaflet-control-zoom-in"
href="/"
title="Zoom in"
role="button"
aria-label="Zoom in"
onClick={zoomIn}
>
+
</a>
<a
className="leaflet-control-zoom-out"
href="/"
title="Zoom out"
role="button"
aria-label="Zoom out"
onClick={zoomOut}
>
−
</a>
</div>
);
};
然后把它放在你喜欢的地方:
const App = () => {
return (
<Provider>
<div style={{ display: "flex", flexDirection: "row" }}>
<Leaflet />
<Zoom />
</div>
</Provider>
);
};
这里是没有钩子的相同方法:
提供商应如下所示:
class Provider extends Component {
state = { map: null };
setMap = map => {
this.setState({ map });
};
render() {
return (
<Context.Provider value={{ map: this.state.map, setMap: this.setMap }}>
{this.props.children}
</Context.Provider>
);
}
}
传单组件将是:
class Leaflet extends Component {
mapRef = createRef(null);
componentDidMount() {
const map = this.mapRef.current.leafletElement;
this.props.setMap(map);
}
render() {
return (
<Map
style={{ width: "80vw", height: "60vh" }}
ref={this.mapRef}
center={[50.63, 13.047]}
zoom={13}
zoomControl={false}
minZoom={3}
maxZoom={18}
>
<TileLayer
attribution='&copy <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png?"
/>
</Map>
);
}
}
现在要访问 compoenntDidMount 的 setMap 函数,您需要执行以下操作:
export default props => (
<Context.Consumer>
{({ setMap }) => <Leaflet {...props} setMap={setMap} />}
</Context.Consumer>
);
其余的请看这里:Demo