javascript - LeafletJS 中 React 与真实 DOM 的配合

标签 javascript reactjs

我正在尝试将 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(); 删除了;方法。

所以:

  1. 移除 DOM“DIV”(通过外部库)是否应该也会自动“卸载”相关的已安装组件?有没有办法做到这一点?

  2. 如果没有,有没有办法在不循环的情况下一起卸载所有 300 多个汽车组件?例如,类似 React.unmountComponents("Car") 的东西。因为我的循环不是完全证明的:当我检索新数据时,一些旧项目仍将保持安装状态,因为它们不会出现在将循环卸载的新列表中。

  3. 关于如何解决这个问题有什么想法吗?每个标记的(非 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 Florencekeen 上的 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 calling React.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/

相关文章:

javascript - Selenium 刷新任何页面直到元素可见 Java

javascript - jquery 将事件类添加到标签

javascript - 为什么内联脚本放在页面底部时会阻塞渲染?

android - react native : wake a locked phone and play a sound

javascript - 无法删除 useEffect 之外的事件监听器

javascript - Typescript - 导入 React 功能组件时出错

javascript,有没有像isArray这样的isObject函数?

javascript - React 功能组件 useEffect 钩子(Hook)在类组件生命周期中具有相等的依赖性

javascript - 创建 React App + Express.js + Passport.js 登录问题

javascript - 从多行文本区域中提取两个单词