5

I have a component that renders Markers on leaflet map. The markers need to change position every time the server sends a new position for one or more markers. How can I change the position of the specific markers that changed its position without re-render all the markers?

I was thinking to use useMemo hook but I didn't succeed to use this hook on map function because hook cannot be called inside callback.


const Participants = () => {
  // This pattern is showed here: https://medium.com/digio-australia/using-the-react-usecontext-hook-9f55461c4eae
  const { participants, setParticipants } = useContext(ParticipantsContext);

  useEffect(() => {
    const socket = io('http://127.0.0.1:8000');
    socket.on('location', data => {
      if (data) {
        const ps = [...participants];
        // currently change the position of the first participant
        ps[0].lat = data.dLat;
        ps[0].long = data.dLong;
        setParticipants(ps);
        console.log(data);
      }
    });
  }, []);


  const renderParticipants = () => {
    return participants.map(p => {
      return (
        <ParticipantIcon key={p.id} id={p.id} position={[p.lat, p.long]}>
          {p.id}
        </ParticipantIcon>
      );
    });
  };
  return <div>{renderParticipants()}</div>;
};


const ParticipantIcon = ({ id, position, children }) => {
  // This is showing if the component rerenderd
  useEffect(() => {
    console.log(id);
  });

  return (
    <MapIcon icon={droneIcon} position={position}>
      {children}
    </MapIcon>
  );
};


The actual results were that every time the socket receives location it re-renders all the participants' icons instead of re-render only the first participant in the array.

Nave
  • 113
  • 2
  • 7

1 Answers1

3

Because you are updating the whole position array every render, the reference to the array representing the previous location and the current location will be different, although the latitude and longitude might be exactly the same. To make it work, wrap PariticpantIcon inside React.memo, then do either one of the followings:

  • Split position into 2 different props, namely lat and long. Then inside ParticipantIcon you can put them back together. This codesandbox explains best.

  • Restructure the participants array. Group lat and long together initially would prevent new reference being created at render phase. This codesandbox demonstrates this.

Bonus: Since the ParticipantIcon component just displays the id, you might as well make it cleaner like this:

const ParticipantIcon = ({ id, position, children }) => {
  // This is showing if the component rerenderd
  useEffect(() => {
    console.log(id);
  });

  return (
    <MapIcon icon={droneIcon} position={position}>
      {id}
    </MapIcon>
  );
};

Brian Le
  • 2,646
  • 18
  • 28
  • 1
    Thank you for your answer! But it's important to use React.memo for the child component in both solutions. you may want to update your answer from - As a result, React.memo wouldn't work. to - It wouldn't work alone without changing the props or restructure the state. Thank you again :) – Nave Jul 23 '19 at 10:17