如何使用 useMemo hook 给孩子记忆

Nav*_*ave 5 javascript reactjs react-hooks

我有一个组件可以在传单地图上呈现标记。每次服务器发送一个或多个标记的新位置时,标记都需要更改位置。如何在不重新渲染所有标记的情况下更改更改其位置的特定标记的位置?

我想使用 useMemo 钩子,但我没有成功地在 map 函数上使用这个钩子,因为钩子不能在回调中调用。


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>
  );
};


Run Code Online (Sandbox Code Playgroud)

实际结果是,每次套接字接收到位置时,它都会重新渲染所有参与者的图标,而不是仅重新渲染数组中的第一个参与者。

Bri*_* Le 3

由于每次渲染都会更新整个position数组,因此对表示先前位置和当前位置的数组的引用将会不同,尽管纬度和经度可能完全相同。要使其正常工作,请将其包裹PariticpantIcon在内部React.memo,然后执行以下任一操作:

  • 分成position2 个不同的 prop,即latlong。然后在里面ParticipantIcon你可以把它们放回一起。这个codesandbox解释得最好。

  • 重组participants数组。分组latlong在一起最初会阻止在渲染阶段创建新的引用。这个codesandbox演示了这一点。

额外好处:由于该ParticipantIcon组件只显示 id,因此您不妨像这样使其更清晰:

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

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

Run Code Online (Sandbox Code Playgroud)