1

In the below code snapshot, there are 5 different state updates called in useEffects; 3 in the first and 2 in the second.

I log my code and see that the screen has only rendered 3 times, so 1 initial render and 2 additional renders.

Interestingly, if I comment out / remove the entire second useEffect, I still get 3 renders, so nothing changes. This leads me to believe that state updates from different useEffects can be batched together?

I am also a little confused why I don't get 6 renders instead (1 initial render + 5 updates), because all my state update calls are in a .then Promise. What I read online is that in e.g. event handler code the updates are batched together but not in case of Promise... https://blog.logrocket.com/simplifying-state-management-in-react-apps-with-batched-updates/#:~:text=By%20default%2C%20React%20batches%20updates,time%20the%20call%20is%20made.

useEffect 1 state updates:

setChatsToDisplay(chats)
setChatsUsersInfo(chatsWithUsersInfo)
setChatsAreInitializing(false)

UseEffect 2 state updates:

setMatchInvites(data)
setMatchInvitesAreInitializing(false)

Full code where these updates are called:

useEffect(() => {
    getUserChats(userId)
    .then((getUserChatsData) => {
      if (getUserChatsData) {
                const chats = {}
                for (i = 0; i < getUserChatsData.length; i++) {
                    const matchedUserInArrayFormat = getUserChatsData[i]['users'].filter((users) => {
            return users !== userId
          })
                    const matchedUser = matchedUserInArrayFormat[0]
                    const message = getUserChatsData[i]['message'][0]['content']
                    chats[matchedUser] = message
                }
        const matchedChatUserIds = getUserChatsData.map(userChat => {
          const arrayWithMatchedChatUserId = userChat['users'].filter((users) => {
            return users !== userId
          })
          return arrayWithMatchedChatUserId[0]
        })
        getUserInfo(matchedChatUserIds, JSON.stringify({'name': 1, 'userId': 1, 'profilePictureURI' : 1}), 10)
        .then((userInfo) => {
          var chatsWithUsersInfo = userInfo.map((user, index) => (
            {'index': index, 'matchedUserId': user.userId, 'matchedUserProfilePictureURI': user.profilePictureURI, 'matchedUserName': user.name}
          ))
                    // Updating the states in this order below appears to render the screen two times with the same chatsToDisplay value, which is required to get previousChatsToDisplay to use the initially retrieved chats as base (not an empty Object). The useEffect that depends on [chatsToDisplay, chatsAreInitializing] can then effectively compare new chats
                    setChatsToDisplay(chats)
                    setChatsUsersInfo(chatsWithUsersInfo)
                    console.log('gonna set to false');
                    setChatsAreInitializing(false)
                })
      }
    })
  }, [])
    
    useEffect(() => {
        getMatchInvites(userId, 10)
        .then((getMatchInvitesData) => {
            const data = getMatchInvitesData.map((match, index) => (
                {'index': index, 'matchedUserId': match['usersInfo'][0]['userId'], 'matchType': match['matchType'], 'matchedUserRating': match['usersInfo'][0]['rating'], 'matchId': match['_id']}
            ))
            setMatchInvites(data)
            // To-do: Check my logic for this screen and how it holds with this included
            setMatchInvitesAreInitializing(false)
        })
    }, [])
Jared Smith
  • 19,721
  • 5
  • 45
  • 83
beyondtdr
  • 411
  • 6
  • 17
  • 1
    In the future please make sure that you either add a language tag to the question (I added JS for you) or else put your code samples in triple backticks with a lang-javascript annotation (or whatever language the code is written in). Otherwise the question won't get syntax highlighting. – Jared Smith Jan 06 '21 at 16:24
  • 1
    There won't be a rerender if the new state is the same as a current state. – marzelin Jan 06 '21 at 18:07

2 Answers2

0

If all those setState methods are from useState calls, I would actually try to go over to using a https://reactjs.org/docs/hooks-reference.html#usereducer. Some discussion on it here: https://stackoverflow.com/a/64942458/14953263

This lets you tackle complex state in a better smoother way, avoiding all these batched updates and potential strange behaviour.

The reducer lets you update on actions, coming from redux, but with a much more lightweight API that lets you use it for certain components

0

State updates are asynchronous and are batched together in general, I don't believe it matters if they are in separate side effects.