我正在尝试将 React 与 leafletjs
库一起用于 map ,但我不确定如何最好地实现这一点。
我知道有一个 react-leaflet
github 项目,但就我而言,我需要使用原始 (javaScript) leafletjs
库(以便使用更多不支持的功能,例如Marker上支持html的divIcon: http://leafletjs.com/reference.html#divicon )
我编写的以下代码绝对不是 React 方式(因为它删除并卸载所有内容,然后重新创建并安装所有内容),但它可以工作(经过数小时的反复试验)。
我在 map 上有大约 300 个标记(汽车),并且根据不同的标准我隐藏了其中一些。我还从服务器检索稍微更新的数据。
问题是,在我使用 unmountComponentAtNode
方法之前,React devtools 显示每次重新渲染时添加了 300 个组件。因此,我曾经拥有 3000 个组件,并且还在不断增长,尽管它们的底层(真实 DOM)DIV 被 window.map.carsLayerGroup.clearLayers(); 删除了;方法。
所以:
移除 DOM“DIV”(通过外部库)是否应该也会自动“卸载”相关的已安装组件?有没有办法做到这一点?
如果没有,有没有办法在不循环的情况下一起卸载所有 300 多个汽车组件?例如,类似 React.unmountComponents("Car") 的东西。因为我的循环不是完全证明的:当我检索新数据时,一些旧项目仍将保持安装状态,因为它们不会出现在将循环卸载的新列表中。
关于如何解决这个问题有什么想法吗?每个标记的(非 react )DOM DIV 需要存在,因此我需要找到一种方法来渲染 React 组件并使用 React 生命周期,而不必卸载它并在每次重新渲染时重新创建它。
谢谢!
componentWillUpdate() {
const {props, state} = this;
const {cars} = props;
cars.map((car,i) => { //Clean the previous React components
try {
React.unmountComponentAtNode(document.getElementById(`s${car.id}`));
}
catch (e) {
//do nothing, continue execution
}});
window.map.carsLayerGroup.clearLayers();
cars.map((car,i) => {
var myIcon = L.divIcon({
//className: '',
iconSize: [24, 24],
html: `<div id="s${car.id}"></div>`,
});
var ang = 45;
var lat = car.lat+0.02220,
lon = car.lon+0.02220;
var marker = L.marker([lat, lon], {icon: myIcon});
window.map.carsLayerGroup.addLayer(marker);
if (myData.filters[`checkCarsOfType${car.type}`])
React.render(<Car key={car.id} car={car} carIndex={i} {...props}/>,document.getElementById(`${car.id}`));
});
最佳答案
Shouldn't the removal of the DOM "DIVs" (by an external library) also automatically "unmount" the related mounted components? Is there a way to do so?
它是 looks like the answer is no 。我知道除了自己监视和检查 DOM 节点之外,没有其他方法可以自动化此操作。
If not, is there a way to unmount all 300+ Car components alltogether without looping? For example, something like React.unmountComponents("Car").
我不这么认为,但我对下一个问题的回答可能会解决这个问题。
Any ideas on how to approach this? The (non-react) DOM DIV per Marker needs to exist, so I need to find a way for the React component to be rendered there and use the React lifecycles without having to unmount it and recreate it on every rerender.
由于 Ryan Florence 是 keen 上的 pointing out ,因此您可以使用 React 组件对几乎任何类型的嵌套、树状数据进行建模。让我们看看您粘贴代码的组件在这种情况下可能是什么样子。 (请注意,为了伪代码,我在这里做了一些假设;另请注意,我对 Leaflet API 不是很熟悉:)
class MyLeafletMapApp extends React.Component {
render() {
<CarMap cars={arrayOfCarsFromSomewhere} />
}
}
class CarMap extends React.Component {
render() {
<LeafletMap>
{this.props.cars.map(this.renderCar)}
<LeafletMap>
}
renderCar(car, idx) {
return <CarIcon key={car.id} index={idx} car={car} />;
}
}
class LeafletMap extends React.Component {
constructor() {
super();
this.state = { mapCreated: false };
}
componentDidMount() {
var mapElement = React.findDOMNode(this.refs.map);
this.map = L.map(mapElement, {...});
this.setState({mapCreated: true});
}
componentWillUnmount() {
// clean up the map
}
render() {
// make sure each child knows about the map;
// don't render children until map exists
var children;
if (this.state.mapCreated) {
var map = this.map;
children = React.Children.map(this.props.children, (child) => {
return child ? React.cloneElement(child, {map: map}) : null;
});
} else {
children = null;
}
return <div ref="map">{children}</div>;
}
}
class CarIcon extends React.Component {
constructor() {
super();
this.state = {
divIconHtml: React.renderToStaticMarkup(
<Car car={this.props.car} carIndex={this.props.index}
)
};
}
componentDidMount() {
this.createMarker();
}
componentWillUnmount() {
this.props.map.removeLayer(this.marker);
}
createMarker() {
if (this.marker) {
this.props.map.removeLayer(this.marker);
}
this.icon = L.divIcon({
iconSize: [24, 24],
html: React.renderToString(
<Car car={this.props.car} carIndex={this.props.index} />
)
});
this.marker = L.marker(this.getLatLng(), {icon: this.icon});
this.props.map.addLayer(this.marker);
}
componentWillReceiveProps(nextProps) {
// `carIsDifferent` not implemented in this example
if (carIsDifferent(nextProps.car, this.props.car) {
this.setState({
divIconHtml: React.renderToStaticMarkup(
<Car car={nextProps.car} carIndex={nextProps.index} />
)
});
}
}
componentDidUpdate(prevProps, prevState) {
if (prevState.divIconHtml !== this.state.divIconHtml) {
// If the HTML changed, we need to recreate the marker.
this.createMarker();
} else {
// otherwise, we can keep the same marker, just move it
this.marker.setLatLng(this.getLatLng());
}
}
getLatLng() {
return [ ... ];
}
render() {
return null; // don't render anything, we attach to the map
}
}
再说一遍,这是相当伪代码,但希望能明白要点。通过将你的 map 描述为组件树,让 React 决定这些组件何时出现/更改/消失,并 Hook 到适当的生命周期 Hook ,我们可以消除大量繁琐的 DOM 管理、循环和重新创建 DivIcons,而无需任何操作。原因(当没有任何改变时)。
复杂性主要集中在 CarIcon
;正如您所知,这是因为 Leaflet DivIcons 只能包含静态 HTML。因此,我们观察汽车的变化,当变化时,使用 renderToStaticMarkup
重新创建 HTML。
应该指出的是,react-leaflet 似乎对 Popup
components 做了同样的事情:
<Popup>
contents are rendered by Leaflet callingReact.renderToStaticMarkup()
, therefore the rendered components have a different context from their owner.
但是,在阅读了 the section on custom layers in the Leaflet docs 后,我相信应该可以创建一个包装完全有状态的 React 组件的层(例如,您不需要渲染为静态 HTML)。实现这样的组件将消除 componentWillReceiveProps
中对歌舞的需要,等等。
关于javascript - LeafletJS 中 React 与真实 DOM 的配合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30751850/