更新键数组对的对象而不发生突变

问题描述 投票:0回答:1

我在我的 useEffect 挂钩之一中使用了此代码片段。但它会改变对象并向我显示要出现两次的消息。

setConversations((prev) => {
  const oldConversation = room in prev ? prev[room] : [];
  const newConversation = [
    ...oldConversation,
    {
      sender: from,
      message: msg,
      time: new Date().toLocaleString(),
    },
  ];
  return { ...prev, [room]: newConversation };
});

我错过了什么?任何建议将不胜感激。

编辑:

自定义效果(接收消息的地方):

export const useSocketEventListener = (
  socket,
  peer,
  setPeersOnConference,
  setPeerIdsOnConference,
  setAvailableUsers,
  availableRooms,
  setAvailableRooms,
  setConferenceId,
  setCallOthersTriggered,
  setTransited,
  calls,
  setCalls,
  setConversations
) => {
  useEffect(() => {
    socket?.on('receiveMessage', (msg, from, room) => {
      // alert(`${msg} from ${from} room ${room}`);
      // let temp = socket.id === room ? from : room;
      setConversations((prev) => { // called twice (expected once to add the message to the state)
        const oldConversation = room in prev ? prev[room] : [];
        const newConversation = [
          ...oldConversation,
          {
            sender: from,
            message: msg,
            time: new Date().toLocaleString(),
          },
        ];
        console.log('calling once');
        return { ...prev, [room]: newConversation };
      });
      console.log('i fire once');
    });
    socket?.on('joinRoomAlert', (socketId, room) => {
      //
    });
    socket?.on('leaveRoomAlert', (socketId, room) => {
      alert(`${socketId} left the room ${room}`);
    });
    socket?.on('receivePeersOnConference', (peersOnConference) => {
      // setPeersOnConference(peersOnConference);
    });
    socket?.on('receiveData', (data) => {
      setAvailableUsers(data.users);
      setAvailableRooms(data.rooms);
    });
    socket?.on('peerEndCall', (peerId) => {
      calls[peerId]?.close();
      setCalls((prev) =>
        Object.keys(prev)
          .filter((key) => key !== peerId)
          .reduce((obj, key) => ({ ...obj, [key]: prev[key] }), {})
      );
      setPeersOnConference((prev) => {
        if (Object.keys(prev).length === 1) {
          setCallOthersTriggered(false);
          setTransited(false);
          return {};
        }
        return Object.keys(prev)
          .filter((key) => key !== peerId)
          .reduce((obj, key) => ({ ...obj, [key]: prev[key] }), {});
      });
    });
    socket?.on(
      'receiveCallOthersTriggered',
      (peerIds, conferenceId, caller) => {
        console.log('peerIds: ', peerIds);
        setConferenceId(socket.id === conferenceId ? caller : conferenceId);
        setCallOthersTriggered(true);
        setPeerIdsOnConference([...peerIds]);
      }
    );
    socket?.on('leaveCallAlert', (leftPeerId) => {
      //
    });
    socket?.emit('fetchData');
    peer?.on('call', async (call) => {
      try {
        setTransited(true);
        const selfStream = await getMedia();
        console.log(peer.id, selfStream);
        setPeersOnConference((prev) => ({ ...prev, [peer.id]: selfStream }));
        call.answer(selfStream);
        call.on('stream', (remoteStream) => {
          console.log(call.peer, remoteStream);
          setPeersOnConference((prev) => ({
            ...prev,
            [call.peer]: remoteStream,
          }));
        });
        call.on('close', () => {
          selfStream.getTracks().forEach((track) => track.stop());
        });
        call.on('error', (e) => console.log('error in peer call'));
      } catch (e) {
        console.log('error while receiving call');
      }
    });
  }, [socket, peer]);
};

发送消息功能:

export const sendMessage = (socket, msg, to, setMessage, setConversations) => {
  socket.emit('sendMessage', msg, to);
  setMessage('');
  setConversations((prev) => { // called once
    const oldConversation = to in prev ? prev[to] : [];
    const newConversation = [
      ...oldConversation,
      {
        sender: socket.id,
        message: msg,
        time: new Date().toLocaleString(),
      },
    ];
    return { ...prev, [to]: newConversation };
  });
};

注意:在App.js文件中使用了自定义钩子。

reactjs react-hooks
1个回答
0
投票

谷歌搜索了一段时间后,我发现了一个在 useEffect 逻辑中处理双重渲染的 hack。可能存在其他方法来解决我的特定问题,但我发现的黑客对我来说效果很好。 由于react-18在开发模式下渲染了两次useEffect钩子,禁用严格模式不会使渲染一次。为此,我们需要使用布尔变量来处理这种情况。

useEffect(() => {
  let ignore = false;
  fetchStuff().then(res => {
    if (!ignore) setResult(res)
  })
  return () => { ignore = true }
}, [])

有关更多详细信息,请查看this博客文章。

© www.soinside.com 2019 - 2024. All rights reserved.