首页 > 解决方案 > React-Leaflet - 更新存储在状态中的 Circle 对象,或者优化 Circles 加载速度

问题描述

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

为了节省每次用户更改某些内容并重新渲染地图时创建 Circles 的时间,我考虑将 Circle 对象存储在 state 中的数组中。然后,当用户更改某些内容时,我想更新 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>
    )

标签: reactjsleafletreact-leaflet

解决方案


将这些CircleMarker组件保持在状态根本不理想。来自 react-leaflet 文档:

默认情况下,这些道具应被视为不可变的,只有在此页面中明确记录为可变的道具才会在更改时影响 Leaflet 元素。

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

这可以通过使用 refs 做得更好。我刚刚在这里回答了一个问题:如何在地图加载时打开特定的弹出窗口? . 您可以看到有关如何结合 refs 和 state 以到达底层传单元素的详细说明。在您的情况下,最好不要将所有这些<CircleMaker>组件都保存在状态变量中。直接在您的组件中使用 a 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,其中包含一个对象。该对象的键是每个对象的唯一 id 值location,该值是底层传单L.circleMarker实例。有了它,您可以在任何这些 refs 上调用传单的setStyle方法。例如,circleRefs['some-unique-id'].setStyle({ fillColor: 'red' })将设置该引用的样式。

通过这种方式,您可以更改这些圆圈的颜色,而无需强制重新渲染。这可以一次在很多圈子上完成,而不会对性能造成巨大影响——而不是卸载(并删除)圈子组件,传单的内部结构会重新着色 svg。

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


推荐阅读