Because my app handles the mobile and desktop views in two different components, I've needed to use useContext API to distribute state/propa across them.
I have a Map component and upon moving from mobile to desktop and vice-versa the Map refreshes.
According to the React docs regarding React memo:
If your component renders the same result given the same props, you can wrap it in a call to React.memo for a performance boost in some cases by memoizing the result. This means that React will skip rendering the component, and reuse the last rendered result.
So at first my thinking was "Why don't I memoize the current zoom
prop of the map?" Because when a different breakpoint is hit (going from mobile or Desktop and vv) it causes a re-render.
But then I read this right below the previous passage:
React.memo only checks for prop changes. If your function component wrapped in React.memo has a useState or useContext Hook in its implementation, it will still rerender when state or context change.
However under that there is this!
By default it will only shallowly compare complex objects in the props object. If you want control over the comparison, you can also provide a custom comparison function as the second argument.
This method only exists as a performance optimization. Do not rely on it to “prevent” a render, as this can lead to bugs.
So I tried it anyway but first I used a map method to get the current zoom and then save it via useContext and local-storage thereby when one enters the mobile or desktop at least the user will get the last zoom entered on the map.
As you can see there is a slight lag.
So this is my implementation: The Map function:
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 © <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <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);
And this the Dashboard, so you can see I'm passing the actual Map
as a prop with the zoom
property:
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} />
</>
);
};
So does anyone have any ideas how I can keep the map props/state intact while traversing the breakpoints between mobile and desktop.