Andrew
Andrew

Reputation: 462

How to execute a sort after loading in data into an array in UseEffect - React Native

I'm trying to create a chat app and there is a small issue. Whenever I load in my messages from firebase, they appear in the chat app in unsorted order, so I'm attempting to sort the messages by timestamp so they appear in order. I can do this if I move the sort and setMessages within onReceive of useEffect, but I feel like this will be pretty inefficient because it sorts and setsMessages a separate time for each message that's retrieved from firebase. I want to just do it all at the end after all the messages are loaded into the array.

Right now with my logs, I get this:

[REDACTED TIME]  LOG      []
[REDACTED TIME]  LOG      pushing into loadedMessages
[REDACTED TIME]  LOG      pushing into loadedMessages

So it's printing the (empty) array first, then loading in messages. How can I make sure this is done in the correct order?

  useEffect(() => {
    // Gets User ID
    fetchUserId(getUserId());

    const messagesRef = firebase.database().ref(`${companySymbol}Messages`);
    messagesRef.off();

    messagesRef.off();
    const onReceive = async (data) => {
      const message = data.val();
      const iMessage = {
        _id: message._id,
        text: message.text,
        createdAt: new Date(message.createdAt),
        user: {
          _id: message.user._id,
          name: message.user.name,
        },
      };
      loadedMessages.push(iMessage);
      console.log('pushing into loadedMessages');
    };
    messagesRef.on('child_added', onReceive);

    loadedMessages.sort(
      (message1, message2) => message2.createdAt - message1.createdAt,
    );

    console.log(loadedMessages);

    return () => {
      console.log('useEffect Return:');
      messagesRef.off();
    };
  }, []);

Upvotes: 0

Views: 547

Answers (2)

Shahar
Shahar

Reputation: 2191

I think that the perspective is a bit off. The right way to do so will be to fetch the firebase data sorted.

Firebase has a built-in sort, although it does come with its limitations.

In my opinion, you sould try something like:

const messagesRef = firebase.database().ref(`${companySymbol}Messages`);

messagesRef.orderByChild("createdAt").on("child_added", function(snapshot) {
  // the callback function once a new message has been created. 
  console.log(snapshot.val()); 
});

And if I may add one more thing, to bring every single message from the down of time can be a bit harry once you've got over a thousand or so, so I would recommend limiting it. that can be achieved using the built-in limit function limitToLast(1000) for example.

Good luck!

Upvotes: 1

Daniel Dimitrov
Daniel Dimitrov

Reputation: 2018

Well, the name of the database is "Realtime Database". You are using the "child_added" listener which is going to be triggered every time a new object gets added to the Messages collection. The onReceive callback should do the sorting - otherwise the messages won't be in the correct order. Yes, that is inefficient for the first load as your "child_added" will most probably be triggered for every item returned from the collection and you'll be repeating sorting.

What you could explore as alternative is to have a .once listener: https://firebase.google.com/docs/database/web/read-and-write#read_data_once the first time you populate the data in your app. This will return all the data you need. After that is complete you can create your "child_added" listener and only listen for new objects. This way onReceive shouldn't be called that often the first time and afterwards it already makes sense to sort on every new item that comes in.

Also have a look at sorting: https://firebase.google.com/docs/database/web/lists-of-data#sorting_and_filtering_data

You might be able to return the messages in the correct order.

And also - if you need queries - look at firestore...

Upvotes: 1

Related Questions