Reactjs React Redux如何在平移时加载地图标记而不重新命名所有内容?

Reactjs React Redux如何在平移时加载地图标记而不重新命名所有内容?,reactjs,redux,react-redux,leaflet,react-leaflet,Reactjs,Redux,React Redux,Leaflet,React Leaflet,我正在使用react+redux和react-lipperv3+API加载标记,以便根据平移行为动态地固定在地图上 我这样做是为了触发“dragend”事件,并计算地图边界、缩放级别和中心,以便创建适当的参数以输入API端点。这样,当用户平移地图时,标记将动态输入 标记数据保存在redux存储中,API调用通过侦听端点更改的useEffect触发 问题在于,似乎在每次平移时都会重新渲染所有标记,这使得应用程序变得紧张和缓慢。我希望只渲染新标记,而不重新渲染旧标记。此外,边界之外的旧标记应该简单地

我正在使用react+redux和react-lipperv3+API加载标记,以便根据平移行为动态地固定在地图上

我这样做是为了触发“dragend”事件,并计算地图边界、缩放级别和中心,以便创建适当的参数以输入API端点。这样,当用户平移地图时,标记将动态输入

标记数据保存在redux存储中,API调用通过侦听端点更改的useEffect触发

问题在于,似乎在每次平移时都会重新渲染所有标记,这使得应用程序变得紧张和缓慢。我希望只渲染新标记,而不重新渲染旧标记。此外,边界之外的旧标记应该简单地删除。然而,事实并非如此。我认为,只要标记有一个键,react redux就能够将旧数据与新数据进行比较,并且只渲染新组件

我的标记加载是通过createAyncThunk完成的,如下所示:

export const getCities = createAsyncThunk("markers/getCities", async (endpoint, thunkAPI) => {
    try {
        const response = await axios.get(endpoint);
        return response.data;
    } catch (error) {
         return thunkAPI.rejectWithValue({ error: error.message });
    }
});
使用以下切片:

// CREATE SLICE
const markerSlice = createSlice({
  name: "markers",
  initialState: {
    cities: [],
    markerType: "cities",
    endpoint: "/api/get/cities",
  },
  reducers: {
  // some reducer code
  },
  extraReducers: (builder) => {
    builder.addCase(getCities.pending, (state) => {
        state.cities = [];
    });
    builder.addCase(getCities.fulfilled, (state, { payload }) => {
        state.cities = payload;
    });
    builder.addCase(getCities.rejected,(state, action) => {
        state.loading = "error";
    });
  }
});
我的地图组件是这样的(为了可读性而简化):

import React,{useffect}来自“React”;
从“react传单”导入{MapContainer、Marker、Popup、TileLayer、useMap、useMapEvents};
导入“../../css/app.css”;
从“react-redux”导入{useSelector,useDispatch,batch};
从“./features/markerSlice”导入{getCities,setEndpoint};
从“./features/maplice”导入{setLatBnd,setLngBnd,setZoom,setLat,setLng};
导出常量LeafMap=()=>{
const dispatch=usedpatch();
//标记状态(标记数据)
const stateMarker=useSelector(state=>state.marker);
//地图状态(中心、缩放级别、边界等)
const stateMap=useSelector(state=>state.map);
//这将调用API来检索数据
useffect(()=>{
调度(getCities(stateMarker.endpoint));
},[stateMarker.endpoint]);
//用于渲染标记的自定义标记组件
常量GetMarkers=()=>{
const markerType=stateMarker.markerType;
返回stateMarker.cities.map((el,i)=>(
));
};
//这是MapContainer(传单v3)的子组件,用于侦听映射事件
函数GetMapProperties(){
const map=useMap();
//监听拖动事件结束
useMapEvents({
德拉根德:()=>{
//获取映射信息以便为API调用创建端点参数
const bounds=map.getBounds();
常量latBnd=边界[''东北'].lat
常量lngBnd=边界[''东北'].lng
const zoom=map.getZoom();
const lat=map.getCenter().lat;
const lng=map.getCenter().lng;
//更新触发API调用的端点(通过顶部的useEffect)
调度(setEndpoint({type:“trees”,lat:lat,lng:lng,latbnd:latbnd,lngbnd:lngbnd}))
},
});
//基于是否有可用数据渲染组件
如果(stateMarker.cities.length>0){
返回(
);
}否则{
返回(
);
}
}

这取决于您拥有的标记的数量。如果标记的数量不太多,可以全部重新命名。但是如果标记太多(数百或数千个)它们占用大量内存,因为它们需要许多dom元素来绘制,这对浏览器来说非常昂贵。提供一个小的演示来重现这个问题会很好。@kboul自己很难制作一个演示,但标记肯定在一个屏幕上有数百个。我见过很多人在一个m上绘制数万个数据点的演示ap没有问题。这是为什么?标记图标有问题吗?我应该用更有效的图标替换图标吗?你可以使用圆形标记、画布选项或标记簇来渲染大量标记。查看更多详细信息我认为你的重新渲染问题是因为
stateMarkers
是一个状态变量可以在
LeafMap
上运行,因此当它发生变化时,整个地图都会重新渲染。我会将
GetMarkers
分解到它自己的组件中,并将所有API和状态逻辑移动到该组件中,并且不会使地图的渲染以该状态变量为条件。无论发生什么情况,只要渲染
GetMarkers
。If
stateMarker.cities。length
为0,这很好,它只会不渲染任何标记。至少这样,当api调用运行时,
GetMarkers
会更新,但不一定会更新整个地图,这取决于您拥有的标记数。如果标记数不太多,则可以重新渲染所有标记。但如果标记数太多(数百或数千)它们占用大量内存,因为它们需要许多dom元素来绘制,这对浏览器来说非常昂贵。提供一个小的演示来重现这个问题会很好。@kboul自己很难制作一个演示,但标记肯定在一个屏幕上有数百个。我见过很多人在一个m上绘制数万个数据点的演示ap没有问题。这是为什么?标记图标有问题吗?我应该用更有效的图标替换图标吗?你可以使用圆形标记、画布选项或标记簇来渲染大量标记。查看更多详细信息我认为你的重新渲染问题是因为
stateMarkers
是一个状态变量可以在
LeafMap
上运行,因此当它更改时,整个映射将重新渲染。我会将
GetMarkers
分解到它自己的组件中,并将所有API和状态逻辑移动到该组件中,并且不使映射的渲染以该状态变量为条件。只需渲染
GetM即可
import React, { useEffect } from "react";
import { MapContainer, Marker, Popup, TileLayer, useMap, useMapEvents } from "react-leaflet";
import "../../css/app.css";
import { useSelector, useDispatch, batch } from "react-redux";
import { getCities, setEndpoint } from "../features/markerSlice";
import { setLatBnd, setLngBnd, setZoom, setLat, setLng } from "../features/mapSlice";

export const LeafMap = () => {
    const dispatch = useDispatch();

    //marker state (marker data)
    const stateMarker = useSelector(state => state.marker);

    // map state (center, zoom level, bounds, etc)
    const stateMap = useSelector(state => state.map);

    // This calls the API to retrieve data
    useEffect(() => {
        dispatch(getCities(stateMarker.endpoint));
    }, [stateMarker.endpoint]);

    // Custom Marker Component to render markers
    const GetMarkers = () => {
        const markerType = stateMarker.markerType;

            return stateMarker.cities.map((el, i) => (
              <Marker
                key={i}
                position={[el.latitude, el.longitude]}
              >
              </Marker>
            ));
    };

    // This is a child component to MapContainer (leaflet v3) which listens to map events
    function GetMapProperties() {
        const map = useMap();
        // listen to drag event end
        useMapEvents({
            dragend: () => {
                // Get map info in order to create endpoint parameters for API call
                const bounds = map.getBounds();
                const latBnd = bounds['_northEast'].lat
                const lngBnd = bounds['_northEast'].lng
                const zoom = map.getZoom();
                const lat = map.getCenter().lat;
                const lng = map.getCenter().lng;
                // update endpoint which triggers API call (via useEffect on top)
                dispatch(setEndpoint({type:"trees", lat:lat, lng:lng, latbnd:latBnd, lngbnd:lngBnd}))
            },
        
        });

    // render component based on if there is available data
    if (stateMarker.cities.length > 0) {
        return (
            <MapContainer preferCanvas={true} center={[stateMap.lat, stateMap.lng]} zoom={stateMap.zoom} scrollWheelZoom={true}>
              <GetMapProperties />
              <TileLayer
                attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
              />
              <GetMarkers />
            </MapContainer>
        );
    } else {
        return (
            <MapContainer center={[stateMap.lat, stateMap.lng]} zoom={stateMap.zoom} scrollWheelZoom={true}>
              <TileLayer
                attribution='&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
                url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
              />
            </MapContainer>
        );
    }

}