2022-10-23

Encountered two children with the same key when requesting a data from fireabase with onChildAdded

  1. Intro

I'm creating a chat application and was trying to get rid of useless messages rerenders in a messages list when requesting them with onValue from firebase (snapshot from onValue was overwriting whole messages list state which caused to a rerender of all messages). Solution i came up with is to use onChildAdded which

is triggered once for each existing child and then again every time a new child is added

I created a useList hook which supposed to return a list of messages on every change of chatId (when switching between chats)

export function useList(chatId) {
  const [list, setList] = useState([]);
  const database = getDatabase();

  useEffect(() => {
    setList([])
    const chatQuery = query(
      ref(database, "/messages/" + chatId),
      orderByValue("timestamp")
    );
    onChildAdded(chatQuery, (data) => {
      setList((prev) => [...prev, { key: data.key, message: data.val() }]);
    });
  }, [chatId]);

  return [list];
}

And it works fine, but... :(

  1. Problem

When i'm changing chatId few times (was in a chat with person A, then switched to the person B, and then back to A) and sending message to A, this last message is displayed twice in a list of messages with a two children with the same key error.

Moreover if im switching between chats more then twice, and then sending a message, the state of this last message seems to be accumulating, and it is displayed as many times as there were switches between chats. All messages are sended to the firebase properly, after page reload everything is fine, no duplicates of last message, only after switching between chats.

  1. Conclusion

I'm fairly new in a react and firebase so, if there is some better way of solving all messages rerenders i would be happy to know about it. Also i would really appreciate explanation of this bug and it's solution :)

My Messages list

function Messages() {
  const chatId = useSelector((state) => state.chat.id);
  const [messages] = useList(chatId);

  return (
    <VStack w="100%" h="100%" p="5" overflowY="scroll" spacing="20px">
      {messages.map(({ key, message }) => {
        return <Message key={key} message={message}/>;
      })}
    </VStack>
  );
}

4 Solution

  useEffect(() => {
    setList([]);
      onChildAdded(chatQuery, (data) => {
        setList((prev) => [...prev, { key: data.key, message: data.val() }]);
      });
    return () => off(chatQuery, 'child_added');
  }, [chatId]);


No comments:

Post a Comment