DEV Community

Lenmor Ld
Lenmor Ld

Posted on • Edited on

What is the standard way to keep UI state and backend state synced during updates? (React and Node)

Let's say i have a Node-Express backend with a REST API and a React frontend that has some editable Card. On App load, I fetch an items array and do a setState

componentDidMount() {
    axios.get('/items').then(() => {
        this.setState({ items: res.data })  // res.data is array of items
    })
}

When user updates the card, I send a PUT or POST request to the backend,
which updates the card details (with or without DB doesnt matter, just assume backend state is updated). good.

My question is, what's the best way to update the UI ?
Is there a standard way when doing REST?

Here are the ideas I could come up with:

  • On my server PUT/POST route handler, i could return items array, including the one updated. On UI, I could then do a simple setState({ items: res.data }). Returning the entire items array seems expensive though, considering only one item is updated.

    • At least backend is source of truth, if update fails, i could easily tell UI error happened.
  • On my server Put/POST route handler, i could return only updated item.
    I would then have to find and replace the updated item in my state and do a setState({ items: res.data })

    • Simple enough, but it feels like i'm doing extra work. But since my data structure is an array, I have to replace entire array in UI state.
    • Or is there a better way to update only one item in an array in state, or maybe better to change my data structure to an object, so setState is optimal.
  • On my server Put/POST route handler, i could return a status code/message to signal UI that operation succeeded and now I can update UI state.

  • On UI side, I do the update first, then send the updates to backend.

    For some reason if it fails in backend, then I rollback UI changes (might be tricky?)

  • Use localStorage in between?

any other ideas?

This is probably handled better in GraphQL and using state management like Flux,
but if i'm doing it really vanilla Node and React, what's the best way?

Let me know if my question isn't clear enough, i could elaborate

Thanks!

Top comments (12)

Collapse
 
owenconti profile image
Owen Conti πŸ‡¨πŸ‡¦ • Edited

Hi Lenmor,

What I would suggest is changing how your store the items in your UI state. For example, instead of storing an array of items, I would store an object, with the keys being the items id property:

setState(_.keyBy(items, 'id'))
Enter fullscreen mode Exit fullscreen mode

Then, I would use your second idea (POST/PUT returns the updated resource). When you go to update the local state after the server responds to the request, you can easily update only the updated item:

setState({
    ...state.items,
    [response.data.id]: response.data
})
Enter fullscreen mode Exit fullscreen mode
Collapse
 
lennythedev profile image
Lenmor Ld • Edited

Thanks! I was used to having an array before but the idea of using Objects is really growing on me. My app would most likely involve a lot of quick changes in the object's attribute, so this makes a lot of sense.

Collapse
 
jesseayegba profile image
JesseAyegba

Amazing insight. The only problem I have with storing data in an object is the inability to loop through the items in that object. Using methods like "map" becomes practically impossible for a react developer.

Collapse
 
jamesjulius profile image
james-julius • Edited

How about writing a custom function? I think something like this would work and would create a nice mini-optimisation to use objects as you're suggesting!

function mapObject(obj, callback) {
for (let [idx, val] in obj.entries()) {
callback(val, idx);
}
}

Collapse
 
owenconti profile image
Owen Conti πŸ‡¨πŸ‡¦

You can get around the loop issue by converting the object to an array before/during render:

Object.values(state.items).map(item => ...)
Enter fullscreen mode Exit fullscreen mode
Collapse
 
avalander profile image
Avalander • Edited

I'm afraid there is no universal answer. All your ideas are potentially good solutions, depending on the particular idiosyncrasies of your app.

On my server PUT/POST route handler, i could return items array, including the one updated.

As you say, the good thing about this is that your backend is the indisputable source of truth and you get the entire current state after an update operation. This might be desirable if you expect multiple users to update the array of items concurrently and it is important that the users don't see stale data. The other advantage is that you need to do less work on the client side. The client can just discard the previous state after an update operation, instead of having to merge the update result with the previous state.

However, the larger your array gets, the less practical this approach becomes. Also, if it is really important to keep the state updated in real time, this approach by itself is not enough.

On my server Put/POST route handler, i could return only updated item.

I'd say this is quite standard strategy.

I don't know a lot about React, but isn't it supposed to take care of only updating the UI nodes that have actually changed when you update the state? This depends a lot on how you are managing your state, but even if you need to set a new array to the state of the component, I don't think there's a huge computational cost involved.

On my server Put/POST route handler, i could return a status code/message to signal UI that operation succeeded and now I can update UI state.

I don't see any advantage in this solution over the previous one. That is, unless the payload you are sending to the server is huge and you need to cut down bandwidth usage, but I'd say that's very unlikely the case.

The advantage of getting the updated item back from the server is that you get any transformation that the server might have performed on the item.

On UI side, I do the update first, then send the updates to backend.

I've seen some applications that do that. I personally think it is bad user experience to act as if something has been updated and then show an error saying that "yeah, it didn't actually go through, sorry about that". I'd rather just have some feedback that the operation is in progress and see the updated state once it has succeeded

This is just my personal opinion, though, if you think this will enhance your user experience, is as valid as any other option. I can see it as a valid approach in an app that behaves synchronously and uses a remote server to back up data, but doesn't depend on it. In that scenario you might even implement a retry system and only cascade the error to the UI when something goes really wrong.

Collapse
 
arpitrathi profile image
Arpit Rathi

Regarding the last approach about updating UI first and showing loading screen and then show if database operation succeeds and revert if database operation fails seems kinda like extra state management and unnecessary tricky rollback work.

A better approach to the last one could be just start updating backend, show a loading scren while thats happening and if it succeeds just do a UI state update.
This will save us from the work of rollback in cases of backend fails

Collapse
 
lennythedev profile image
Lenmor Ld

Thanks for the very detailed response
At this point, the project is in its early stages, and all of these ideas will definitely help me plan the project better

Collapse
 
ezemans profile image
Ezequiel Mansilla

"On UI side, I do the update first, then send the updates to backend". This is the right way.

Collapse
 
lennythedev profile image
Lenmor Ld

I'm starting to see this very often, and recently learned that it's called "optimistic rendering/UI updates"

Definitely a pattern to explore

Collapse
 
jesseayegba profile image
JesseAyegba

What if the request never gets to the backend? How do you deal with that? It means that your UI is now in a wrong state.

Collapse
 
yifeikong profile image
Yifei Kong

I suppose that would be a timeout error?