在 React 选项卡组件中管理 OpenLayers 地图
Managing an OpenLayers Map in a React Tab Component
我正在构建一个 SPA 应用程序,它带有一个带有各种选项卡的导航栏。当一个选项卡被 selected 时,我通过 redux 调度一个动作,并且 AppBody
组件根据哪个选项卡被 selected 重新呈现自己(参见 renderAppBody()
in AppBody
).
我的问题是其中一个选项卡必须包含 OpenLayers 地图。一开始我在加载地图时遇到了很多麻烦,因为它必须以 DOM 上已经存在的元素为目标。我通过覆盖 componentDidMount
:
来做到这一点
MapBody.js
import React from 'react';
import "ol/ol.css"
import Map from 'ol/map'
import View from 'ol/view'
import Tile from 'ol/layer/tile'
import OSM from 'ol/source/osm'
import "../../sass/App.scss";
var map = new Map({
layers: [
new Tile({
source: new OSM()
}),
],
view: new View({
center: [0, 0],
zoom: 4
})
});
const MapBody = React.createClass({
render: function () {
return (<div id="map"></div>);
},
componentDidMount: function () {
map.setTarget("map");
}
});
export default MapBody;
这意味着每当我切换选项卡时,我的 Map 元素都会被销毁/删除并替换为下一个选项卡的内容。结果是,我第一次 select 它呈现的地图选项卡,但如果我然后 select 另一个选项卡然后重新 select 地图选项卡,地图将不会呈现。
对于我想要的结果,我的方法是否存在固有缺陷?这是我的第一个大型 React 项目,所以我不确定设计这种行为的最佳方法。
我应该:
- 维护对地图项的引用并在重新渲染时重新附加它? (似乎有问题)
- 隐藏选项卡而不是替换它们? (似乎也有问题)
- 使MapBody有两种状态:
visible
和inactive
。然后总是将它渲染到屏幕上,并根据它的状态用 CSS 隐藏它(看起来很老套)
- 更优雅的东西?
AppBody.js
import React from 'react';
import { connect } from 'react-redux';
import Activity from '../components/body/Activity'
import CategoryBody from '../components/body/CategoryBody'
import MapBody from '../components/body/MapBody'
import Search from '../components/body/Search'
import tabs from "../actions/tabDefinitions.js"
import "../sass/App.scss";
function renderAppBody(activeTab) {
switch (activeTab) {
case tabs.T_CATEGORIES:
return <CategoryBody />
case tabs.T_MAP:
return (<MapBody />)
case tabs.T_SEARCH:
return <Search />
case tabs.T_ACTIVITY:
return <Activity />
default:
return <div>Oops! Something went wrong :(</div>
}
}
const AppBody = ({activeTab}) => (
<div className="frame_wrapper">
{renderAppBody(activeTab)}
</div>
)
const mapStateToProps = (state) => ({
activeTab: state.activeTab,
});
export default connect(mapStateToProps)(AppBody);
谢谢!
我最终总是将 MapBody
组件添加到 AppBody
,而不考虑选择的选项卡。然后我向 MapBody
添加了一个 active
道具以添加显示/隐藏 CSS 类。这样,当更改选项卡时,绝不会卸下 /重置地图容器,因此状态可以很好地维护。我的方法感觉不是特别优雅(阅读:这是一个肮脏、肮脏的 hack),但它仍然有效。
我的 MapBody
渲染函数变成了:
render: function () {
return (<div id="map"
className={this.props.active ? "active_map" : "inactive_map"}>
</div>);
}
通过添加以下 CSS mostly 给出了所需的行为:
.inactive_map {
display: none;
}
这导致地图容器在切换选项卡时可以正确呈现/隐藏,但地图 canvas 是灰色的,没有呈现任何内容。为了解决这个问题,我覆盖了 componentDidUpdate()
以调用 map.updateSize()
,这会强制重新计算地图视口大小。最终代码:
MapBody.js
const MapBody = React.createClass({
render: function () {
return (<div id="map"
className={this.props.active ? "active_map" : "inactive_map"}>
</div>);
},
componentDidMount: function () {
this.props.map.setTarget("map");
},
componentDidUpdate: function () {
this.props.map.updateSize();
}
});
AppBody.js
function renderAppBody(activeTab) {
switch (activeTab) {
case tabs.T_CATEGORIES:
return <CategoryBody />
case tabs.T_MAP:
return "";
case tabs.T_SEARCH:
return <Search />
case tabs.T_ACTIVITY:
return <Activity />
default:
return (<div>Oops! Something went wrong :(</div>)
}
}
const AppBody = ({activeTab}) => (
<div className="frame_wrapper">
{renderAppBody(activeTab)}
<Map active={activeTab == tabs.T_MAP}/>
</div>
)
我正在构建一个 SPA 应用程序,它带有一个带有各种选项卡的导航栏。当一个选项卡被 selected 时,我通过 redux 调度一个动作,并且 AppBody
组件根据哪个选项卡被 selected 重新呈现自己(参见 renderAppBody()
in AppBody
).
我的问题是其中一个选项卡必须包含 OpenLayers 地图。一开始我在加载地图时遇到了很多麻烦,因为它必须以 DOM 上已经存在的元素为目标。我通过覆盖 componentDidMount
:
MapBody.js
import React from 'react';
import "ol/ol.css"
import Map from 'ol/map'
import View from 'ol/view'
import Tile from 'ol/layer/tile'
import OSM from 'ol/source/osm'
import "../../sass/App.scss";
var map = new Map({
layers: [
new Tile({
source: new OSM()
}),
],
view: new View({
center: [0, 0],
zoom: 4
})
});
const MapBody = React.createClass({
render: function () {
return (<div id="map"></div>);
},
componentDidMount: function () {
map.setTarget("map");
}
});
export default MapBody;
这意味着每当我切换选项卡时,我的 Map 元素都会被销毁/删除并替换为下一个选项卡的内容。结果是,我第一次 select 它呈现的地图选项卡,但如果我然后 select 另一个选项卡然后重新 select 地图选项卡,地图将不会呈现。
对于我想要的结果,我的方法是否存在固有缺陷?这是我的第一个大型 React 项目,所以我不确定设计这种行为的最佳方法。
我应该:
- 维护对地图项的引用并在重新渲染时重新附加它? (似乎有问题)
- 隐藏选项卡而不是替换它们? (似乎也有问题)
- 使MapBody有两种状态:
visible
和inactive
。然后总是将它渲染到屏幕上,并根据它的状态用 CSS 隐藏它(看起来很老套) - 更优雅的东西?
AppBody.js
import React from 'react';
import { connect } from 'react-redux';
import Activity from '../components/body/Activity'
import CategoryBody from '../components/body/CategoryBody'
import MapBody from '../components/body/MapBody'
import Search from '../components/body/Search'
import tabs from "../actions/tabDefinitions.js"
import "../sass/App.scss";
function renderAppBody(activeTab) {
switch (activeTab) {
case tabs.T_CATEGORIES:
return <CategoryBody />
case tabs.T_MAP:
return (<MapBody />)
case tabs.T_SEARCH:
return <Search />
case tabs.T_ACTIVITY:
return <Activity />
default:
return <div>Oops! Something went wrong :(</div>
}
}
const AppBody = ({activeTab}) => (
<div className="frame_wrapper">
{renderAppBody(activeTab)}
</div>
)
const mapStateToProps = (state) => ({
activeTab: state.activeTab,
});
export default connect(mapStateToProps)(AppBody);
谢谢!
我最终总是将 MapBody
组件添加到 AppBody
,而不考虑选择的选项卡。然后我向 MapBody
添加了一个 active
道具以添加显示/隐藏 CSS 类。这样,当更改选项卡时,绝不会卸下 /重置地图容器,因此状态可以很好地维护。我的方法感觉不是特别优雅(阅读:这是一个肮脏、肮脏的 hack),但它仍然有效。
我的 MapBody
渲染函数变成了:
render: function () {
return (<div id="map"
className={this.props.active ? "active_map" : "inactive_map"}>
</div>);
}
通过添加以下 CSS mostly 给出了所需的行为:
.inactive_map {
display: none;
}
这导致地图容器在切换选项卡时可以正确呈现/隐藏,但地图 canvas 是灰色的,没有呈现任何内容。为了解决这个问题,我覆盖了 componentDidUpdate()
以调用 map.updateSize()
,这会强制重新计算地图视口大小。最终代码:
MapBody.js
const MapBody = React.createClass({
render: function () {
return (<div id="map"
className={this.props.active ? "active_map" : "inactive_map"}>
</div>);
},
componentDidMount: function () {
this.props.map.setTarget("map");
},
componentDidUpdate: function () {
this.props.map.updateSize();
}
});
AppBody.js
function renderAppBody(activeTab) {
switch (activeTab) {
case tabs.T_CATEGORIES:
return <CategoryBody />
case tabs.T_MAP:
return "";
case tabs.T_SEARCH:
return <Search />
case tabs.T_ACTIVITY:
return <Activity />
default:
return (<div>Oops! Something went wrong :(</div>)
}
}
const AppBody = ({activeTab}) => (
<div className="frame_wrapper">
{renderAppBody(activeTab)}
<Map active={activeTab == tabs.T_MAP}/>
</div>
)