React-Leaflet - 更新存储在状态中的 Circle 对象,或者优化 Circles 加载速度

React-Leaflet - Updating Circle objects that are stored in state, or alternatively optimizing Circles loading speed

我正在尝试创建一个包含大量圆形对象的地图。这些圆圈的颜色会根据用户输入而改变。可以一次改变很多颜色,我想尽快呈现变化。

为了节省每次用户更改内容和地图重新渲染时创建 Circles 的时间,我考虑将 Circle 对象存储在状态数组中。然后当用户更改某些内容时,我想更新 Circles 的属性,但不使用复制方法等(因为它与仅创建一次 Circle 对象的想法相矛盾)。

我考虑过制作一个存储颜色的并行数组,该数组将由用户更新,并在每个 Circle 对象的 pathOptions 中存储对该数组中并行位置的引用,但我不确定该怎么做这个。

或者,我很乐意听到有关如何优化速度的任何其他说明。

基本版本,应用程序从数组中正确加载圆圈,颜色是静态的:

import locations from "../locations.json"

function Map(props){
    const [circlesArray, setCirclesArray] = useState([])

    useEffect(() => { //initializes the circlesArray
        let tempCirclesArray = []
        locations.map(location => {
            let position = [location.coordinates[1], location.coordinates[0]]
            tempCirclesArray.push(
                <CircleMarker center={position} radius={4}
                pathOptions={
                   color: "red",
                   fillColor: "red"
                   } //here pathOptions is predetermined
                />
            )
        })
        setCirclesArray(tempCirclesArray)
    }, [])

return(
        <div>
            <div id="mapid" ref={mapRef}>  
                <button onClick={clickTest}>helno</button>
                <MapContainer center={defaultPositon} zoom={mapZoom} scrollWheelZoom={true}>
                    <TileLayer
                        attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                        url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
                    />

                    //map and display the Circle objects
                    {
                        circlesArray.map(circle => {
                            return(circle)
                        })
                    }

                </MapContainer>
            </div>
        </div>
    )

将这些 CircleMarker 组件保持在状态中一点也不理想。来自 react-leaflet 文档:

By default these props should be treated as immutable, only the props explicitely documented as mutable in this page will affect the Leaflet element when changed.

更改这些道具不会重新渲染它们。

使用 refs 可以做得更好。我刚刚在这里回答了一个问题:How to open a specific popup on map load? 。您可以看到有关如何结合 refs 和 state 以到达底层传单元素的详细说明。在您的情况下,最好不要将所有这些 <CircleMaker> 组件保存在状态变量中。直接在您的组件中使用 locations.map,并在该映射中将所有引用保存到一个对象:

import locations from "../locations.json"

const MyMapComponent = {

  const circleRefs = React.useRef()

  return (
    <MapContainer {...MapContainerProps}>
      <TileLayer />
      {locations.map(location => {
        let position = [location.coordinates[1], location.coordinates[0]]
        return (
          <CircleMarker 
            preferCanvas
            center={position} 
            radius={4}
            pathOptions={...options_you_want} 
            ref={(ref) => {
              circleRefs.current[location.id] = ref;
            }}
          />
        )
      })}
    </MapContainer>
  )
}

现在您有一个对象,circleRefs,其中包含一个对象。该对象的键是每个 location 的唯一 id 值,值是任何这些 refs 上的 底层传单 L.circleMarker instance. With that, you can call leaflet's setStyle 方法。例如,circleRefs['some-unique-id'].setStyle({ fillColor: 'red' }) 将设置该引用的样式。

通过这种方式,您可以在不强制重新渲染的情况下更改这些圆圈的颜色。这可以同时在很多圈子上完成,而不会造成巨大的性能损失 - 而不是卸载(和删除)圈子组件,传单的内部结构会重新着色 svg。

我还添加了 preferCanvas 道具,对于任何扩展 L.path(包括 L.circleMarker)的东西,选择使用 canvas 而不是 svgs。对于大量项目,这也有助于提高性能。