I'm interested in opinions on how to manage global client state (Zustand) invalidation as a result of a socket update which modifies the network state cache (Apollo).
Let's say we have the following state:
Apollo cache (requested from server):
{ posts: [
{ id: 1, text: 'Hello World!' },
{ id: 2, text: 'I has a bucket!' },
{ id: 3, text: 'Thanks StackOverflow!' }
] }
Zustand store (client state):
{
selectedPost: 2,
editMode: true
}
In this state, we show a UI which allows editing the contents of post 2. We run into an edge case however, if another user deletes post 2. A socket update will remove that post from our network cache, while our client state still believes it is selected and being edited.
This could result in a strange user experience where the post has disappeared, but they still see the edit UI, and any attempt to save will fail, since the resource no longer exists to be updated on the backend.
The expectation is to set the following client state if the selected feedback is removed:
{
selectedPost: null,
editMode: false,
}
The full situation is more complex, and I'd want to show a message to the user, but I feel those details are unimportant to this discussion, so I've minimized the state as much as possible.
I've considered a few options for solving this problem. Each with some pros and cons. Any one of these can work. What I'm interested in is advice from someone familiar with this type of problem, on which solution is most viable long-term.
- useEffect
Write a useEffect which listens for posts not including a post with post.id === selectedPost
, and updates client state to { selectedPost: null, editMode: false }
.
This has the disadvantage that there is an in-between render where the post does not exist, but it is still selected and we still show the edit UI. Meaning we have to be sure our components can handle that invalid state. It could also result in a visual flicker where the user sees this invalid UI briefly before the client state is adjusted.
- Side-effect of Apollo cache change
I'm not sure if this is something that can be done.
Write a callback which runs when the Apollo cache changes, but before the resulting render occurs. This callback checks if selectedPost
exists in the new cache. If not, it updates the client state. This would not cause the in-between invalid render.
I haven't seen anything on the Apollo docs about this. So I'm not sure if it's possible.
- Update client state in the socket handler, at the same time Apollo cache is updated
We have functions that handle each socket event, which make the required modifications to the Apollo cache. We could add the client state checks and updates here. This would avoid the invalid render problem.
The downside is we would have to manually handle every socket event that could result in removing a post. Which would result in duplicated code and room for error if new socket events were introduced.
Does anyone have experience with updating client state as a side-effect of network state like this? Any suggestions on how to handle it elegantly without the invalid in-between render?