skip to Main Content

I am making an API call every 1 minute to get real-time effect. When the data comes back from the api, it’s likely that some items will have changed. I am not sure whether to replace the entire list of array with the new array that came back from API or just update what was changed. The array can contain up to 100 items. Example,

const [users,setUsers] =useState<{name:string,last_online:string}>([])

useEffect(()=> {
 // Some logic to run this every 1 minute
 fetchUsers().then(users => setData(users))
// Here, replace the entire array with users or just find what was changed and update?
// last_online is what will be changed and I need to update this

},[])

2

Answers


  1. According to setState docs:

    The set function returned by useState lets you update the state to a different value and trigger a re-render. You can pass the next state directly, or a function that calculates it from the previous state

    nextState: The value that you want the state to be. It can be a value of any type, but there is a special behavior for functions.

    So, when you pass the next value of the state and it will replace previous value. It means that you simply need to pass the new array without finding what changed.

    Login or Signup to reply.
  2. I think you have several options and each on them is valid in a certain use case – it is up to you to decide, which option is best in your case:

    Replace the users array with a new array on every API call:

    useEffect(()=> fetchUsers().then(users => setData(users)), []);
    

    This is the easiest solution and if you don’t have any problems with performance or unnecessary re-renders of some components, it is fine to do that. Just keep in mind to use some kind of ID to identify your user objects when iterating over them, rather than their position in the array, since it can change over time:

    {
       data.map(user => <User key={user.id} ... />)
    }
    

    Replace the users array with a new array that keeps existing user objects in place

    useEffect(()=> fetchUsers().then(users => 
       setData(prevUsers => users.map(
          newUser => prevUsers.some(prevUser => 
             JSON.stringify(prevUser) === JSON.stringify(newUser))
                ? prevUser
                : newUser
          )
        )
      ), []);
    

    This is more complicated and requires more computation. Components that rely on the data of an individual user can be prevented from re-rendering. Components that display the whole users list will still need to re-render. User objects can be compared with ===.

    Replace the users array with a new object that keeps existing user objects in place

    useEffect(()=> fetchUsers().then(users => 
       setData(prevUsers => users.reduce(
          (usersById, newUser) => {
             const prevUser = usersById[newUser.id]
             return {
                ...usersById,
                [newUser.id]: JSON.stringify(prevUser) === JSON.stringify(newUser))
                ? prevUser
                : newUser
             }
          }, 
       data ?? {})), 
       []
    );
    

    This would be useful if you typically care about individual user objects more than about the whole list since you can access an individual object more efficiently. Deletions would not be reflected in the users object.

    Not knowing more about your specific use case, my advice would be for you to go option number one. Choose one of the other options if you have a good reason for it – more often than not, simplicity is more important than efficiency.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search