防止 React-Leaflet 不必要地重新渲染 Map 组件

时间:2021-02-01 16:46:08

标签: javascript reactjs leaflet react-leaflet react-memo

因为我的应用在两个不同的组件中处理移动和桌面视图,所以我需要使用 useContext API 在它们之间分发状态/传播。

我有一个地图组件,当从移动设备移动到桌面设备时,地图会刷新。

根据关于 React 备忘录的 React 文档:

<块引用>

如果你的组件在给定相同的 props 的情况下呈现相同的结果,你 可以将其包装在对 React.memo 的调用中,以提高某些方面的性能 通过记忆结果的案例。这意味着 React 将跳过 渲染组件,并重用上次渲染的结果。

所以一开始我的想法是“为什么我不记住地图的当前 zoom 道具?”因为当遇到不同的断点(来自移动设备或桌面和 vv)时,它会导致重新渲染。

但后来我在上一段的正下方读到了这个:

<块引用>

React.memo 只检查 prop 的变化。如果您的功能组件 包裹在 React.memo 中的有一个 useState 或 useContext Hook 实现时,它仍然会在状态或上下文发生变化时重新渲染。

但是下面有这个!

<块引用>

默认情况下,它只会浅比较 props 中的复杂对象 目的。如果您想控制比较,您还可以提供 自定义比较函数作为第二个参数。

此方法仅作为性能优化存在。不要依赖 它是为了“防止”渲染,因为这会导致错误。

所以我还是尝试了一下,但首先我使用了一种地图方法来获取当前的缩放比例,然后通过 useContextlocal-storage 保存它,这样当一个人进入移动或桌面时,至少用户会得到在地图上输入的最后一个缩放比例。

from desktop to mobile

如您所见,有轻微的延迟。

这是我的实现: 地图功能:

function currentMapViewPropsAreEqual(prevProps, nextProps) {
  return (
    prevProps.currentMapView === nextProps.currentMapView &&
    prevProps.Map === nextProps.Map &&
    prevProps.TileLayer === nextProps.TileLayer
  );
}

function MyMap({ currentMapView, Map, TileLayer }) {
  var [animate, setAnimate] = useState(false);
  var [userLocation, setUserLocation] = useState(null);
    
  var handleWaypointsOnMapRef = useRef(handleWaypointsOnMap);

  var mapRef = useRef();
  var mapRefForRoutingMachine = useRef();
  var { state } = userState();
  var { dispatch } = userDispatch();
  var {
    currentMap,
    isRoutingVisible,
    removeRoutingMachine,
    isLengthOfMarkersLessThanTwo,
    markers
  } = state;

  useEffect(() => {
    handleWaypointsOnMapRef.current = handleWaypointsOnMap;
  }); // update after each render
    
  function handleOnViewportChanged(e) {
    var { current = {} } = mapRef;
    var { leafletElement: map } = current;

    map.on('zoomend', function() {
      var zoom = map.getZoom();

      dispatch({
        type: 'setMapZoom',
        payload: {
          currentMapView: zoom
        }
      });
    });
  }

  function handleOnLocationFound(e) {
    var { current = {} } = mapRef;
    var { leafletElement: map } = current;
    map.setZoom(currentMapView);

    var latlng = e.latlng;
    var radius = e.accuracy;
    var circle = L.circle(latlng, radius);
    circle.addTo(map);
  }

  return (
    <Map
      preferCanvas={true}
      id="myMap"
      animate={animate}
      zoom={currentMapView}
      ref={mapRef}
      onViewportChanged={handleOnViewportChanged}
      onClick={e => handleWaypointsOnMap(e)}
    >
      <TileLayer
        url={`https://api.mapbox.com/styles/v1/${process.env.MAPBOX_USERNAME}/${
          process.env.MAPBOX_STYLE_ID
        }/tiles/256/{z}/{x}/{y}@2x?access_token=${process.env.MAPBOX_ACCESS_TOKEN}`}
        attribution='Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery &copy; <a href="https://www.mapbox.com/">Mapbox</a>'
      />

      {mapRef && (
        <Routing
          isRoutingVisible={isRoutingVisible}
          ref={mapRefForRoutingMachine}
          markers={markers}
          stringify={stringify}
          isLengthOfMarkersLessThanTwo={isLengthOfMarkersLessThanTwo}
          removeRoutingMachine={removeRoutingMachine}
          userLocation={userLocation}
        />
      )}
    </Map>
  );
}

var MemoizedMyMap = React.memo(MyMap, currentMapViewPropsAreEqual);

这是仪表板,因此您可以看到我将实际的 Map 作为带有 zoom 属性的道具传递:

import { Map, TileLayer } from 'react-leaflet';

import { userState, userDispatch } from '../components/Context/UserContext.jsx';

const MyMap = dynamic(() => import('../components/Map/MyMap.jsx'), {
  ssr: false
});

var Dashboard = ({ props }) => {
  var { state } = userState();
  var { dispatch } = userDispatch();
  var { currentMapView } = state;
  return (
    <>

              <MyMap Map={Map} TileLayer={TileLayer} currentMapView={currentMapView} />
            
    </>
  );
};

那么有没有人知道如何在移动和桌面之间遍历断点时保持地图道具/状态完整无缺。

0 个答案:

没有答案