2

I'm showing a map that is memoized using React.memo(). I want the map to only re-render when the props change, but it re-renders even when they don't. How can I prevent the re-renders when props don't change?

The Map:

const MapContainer = React.memo((props) => { const [map, setMap] = useState(); var bounds = new props.google.maps.LatLngBounds(); const setMapObj = (mapProps, map) => { setMap(map); }; useDeepCompareEffect(() => { if (props.markers.length === 1) { bounds.extend( new props.google.maps.LatLng(props.markers[0].lat, props.markers[0].lng) ); bounds.extend( new props.google.maps.LatLng( props.markers[0].lat - 1, props.markers[0].lng - 1 ) ); map && map.fitBounds(bounds); return; } for (var i = 0; i < props.markers.length; i++) { bounds.extend( new props.google.maps.LatLng(props.markers[i].lat, props.markers[i].lng) ); } if (map) { map.fitBounds(bounds); } }, [props.markers, map]); return ( <div className={props.className}> <Map google={props.google} style={{ borderRadius: "10px" }} // center={{ lat: 59.913, lng: 10.75 }} initialCenter={{ lat: 59.913, lng: 10.75 }} onReady={setMapObj} zoom={8} > {console.log("rendered")} {props.markers.map((item, index) => { if (item.lat && item.lng) return ( <Marker key={index} position={{ lat: item.lat, lng: item.lng }} /> ); })} </Map> </div> ); }); export default GoogleApiWrapper({ apiKey: "API_KEY", })(MapContainer); 

Since the parent component is pretty big, I've only added in the parts I think are relevant. The parent of Map:

function Home() { const [plants, setPlants] = useState([ { name: "Plant 1", id: uuidv4(), location: "", coords: {}, country: "", isOffshore: false }, ]); const isObjectEmpty = (obj) => { for (var i in obj) return false; return true; }; const getMarkers = () => { var temp = []; for (var i = 0; i < plants.length; i++) if (!isObjectEmpty(plants[i].coords)) temp.push(plants[i].coords); return temp; }; return ( <section/> <CustomMap className="map" markers={getMarkers()} /> <section/> ) } 

1 Answer 1

4
  1. Memo does not make a deep comparison.
  2. Each time home is re-render, the map will be re-render too. Because you are passing markers as the result of executing a function that each time generates a new array, a new reference, to a different object. Which of course is perceived by memo as a trigger for a re-render. In addition to the new array, keep in mind that the getMarkers function is also new each time and refers to a different object. For functions, useCallback is usually used.
  3. I propose to use the useMemo hook to fix and exclude the creation of a new array, and, accordingly, a reference to a new object. And accordingly there will be no re-rendering when it is not needed.
  4. Don't forget to include dependencies in useMemo. In your case it is plants.

Example

const markers = useMemo(() => { var temp = []; for (var i = 0; i < plants.length; i++) if (!isObjectEmpty(plants[i].coords)) temp.push(plants[i].coords); return temp; }, [plants]); 
Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.