jsheroes/community-help

Redux: synchronizing state among many users

danburzo opened this issue · 7 comments

When building a React/Redux application that contains an amount state that is shared between many users, what is a good approach to synchronize the state so that when user A does action X, user B's state also updates to reflect the action, without having to refresh the app?

I'm thinking something along the lines of a Redux middleware that intercepts all actions and passes them over via Web Sockets to all the subscribed users. On the receiving end, we listen on the connection and dispatch the actions as they come -- I wonder if there's a library that deals specifically with this, and might have some extra subtlety implemented around dealing with 'conflicting' actions, etc.

Web Sockets notifications? User A does action X, server notifies user B. User B requests latest data (not a full refresh). Just a thought.

@dariusbaba ah, yes; that might forego some of the complexity involved with merging actions -- instead of trying to patch the state on the receiving end, we just update a part of the state tree via API calls. Thanks for the idea! 🤔

(If I imagine things correctly, on the receiving end you'd need a function that maps incoming actions to the appropriate refresh action)

We use an Immutable Js map as datastore and have a generic update function. Then we use the deepMerge method on the immutable map to update the store. For example:

store : {
collection1 :  new Map() ,
collection2:  new Map(),
}

Any backend call return data following the convention above. If we have new data on collection1 on the server, the call will return an object of the form:

{
collection1: Map(new data),
}

We have a generic update function that is called for all the incommig actions and refresh the datastore. So there is no need to map to appropriate refresh action.

I played a little with RethinkDB and Horizon and I think it might be a great solution for your use because it's observable-based from the client to the database. However, it's still in its infancy and I wouldn't start a critical project with this setup today.

I would handle the updates on the backend and send via websockets to the frontend the updated data. Something along this lines:

  1. All users are connected via websockets and are ready to receive updates from server.
  2. User does action A
  3. Backend is updated and notifies via websockets other users which need the updated data.
  4. Receivers update their state by turning the data received from websockets into actions => updated state

I would not get into any implementation details (for now) but this is the main idea.

Firebase, horizon etc do this automatically, but there is not reason you cannot do it manually for a few actions (and not have to change your technology stack)

This short article describes how one might implement a Websocket communication layer between instances: https://medium.com/@Scarysize/syncing-redux-stores-across-browser-tabs-fff04f975423

I can imagine various ways of doing the communication:

Simple pipeline
The WebSocket acts as a simple transport layer for actions, relaying them over the wire:

Alice dispatches action -> Action gets serialized -> Action is transported via WS -> Action gets deserialized -> Bob dispatches action

(Of course, we'll want to make a distinction between local actions and remote actions, to avoid the Alice <-> Bob infinite ping-pong)

WS as action to refresh action transformer

Alice dispatches action -> Action gets serialized -> Back-end maps action to refresh action -> Refresh action gets deserialized -> Bob dispatches refresh action

This avoids the possible problem with infinite ping-pong, but bleeds implementation details (action definitions, and their relationships) to the back-end.

WS as action to updated data transformer

Same approach as above, but instead of returning refresh actions, it returns the updated (sub-)state that can then be merged into Bob's state directly.

(P.S. @alexnm thanks for the pointers, sounds interesting!)

I don't know if it fits your specific needs but firebase does a good job on this one.