Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redux+Websockets: Why manage this using middleware?

I've been reading about the best way to integrate WebSockets into a React/Redux app, and I'm finding answers but with some sentence along the lines of "The best place for websocket implementation is usually middleware."

My question is WHY this is preferred? What is the benefit of doing this vs setting up the websocket/having listeners dispatch actions in the outer App-level React container (in componentWillMount for instance)?

This seems like it would be equivalent in terms of lasting throughout the lifecycle of the app, etc. What am I missing here?

like image 698
kasceled Avatar asked Sep 25 '17 10:09

kasceled


People also ask

Why we are using middleware in Redux?

Redux Middleware allows you to intercept every action sent to the reducer so you can make changes to the action or cancel the action. Middleware helps you with logging, error reporting, making asynchronous requests, and a whole lot more.

What is WebSocket middleware?

The middleware is passed an implementation of WebSocketHandler and a RequestDelegate . If the request is not a WebSocket request, it is short-circuiting the middleware and return. If it is a WebSockets request, then it accepts the connection and passes the socket to the OnConnected method from the WebSocketHandler .

Which middleware is best in Redux?

Redux thunk is the most popular middleware that allows you to call action creators, which then returns a function instead of an action object.

What is the purpose of WebSockets?

The WebSocket API is an advanced technology that makes it possible to open a two-way interactive communication session between the user's browser and a server. With this API, you can send messages to a server and receive event-driven responses without having to poll the server for a reply.


2 Answers

There are couple of pros with placing such logic in middle-wares instead of actual components.
The main reasons in my opinion are:

  • Each connected component will instantiate a WebSocket or you will need a global declaration of the connection which will be independently from the store, in other words, not part of the redux flow.
  • Middle-Wares have access to the store and are part of the redux flow.
  • You also get access to the entire store, hence you can pass forward more data then initially dispatched.
  • You decouple the components from the web-socket logic, hence you can centralize your code and reuse your components.

All in all, there are no special reasons that are specific regarding using web-sockets with middle-wares, using middle-wares have great advantages in general.

Edit
As a followup to your comment

How would you suggest managing a case where you might want a particular component to initialize a websocket connection, but want to manage whether it's already connected, etc... would it be just as simple as having a flag in the store that says it's connected or is there a better approach?

As i said, I wouldn't initialize a web-socket connection within a component, rather I would do it in the entry point of my application. like index.js for example.
If your concern is to make sure you won't try to connect when there is already a connection, then a simple socketStart method that get invoked at the point when you create the store and initialize all your App data, you can pass it a callback that will do the rendering and store update through dispatch.
A simple example (keep in mind this is a pseudo code ):

Our Socket-start Method:

export function socketStart(store, callback) {
  // this is only a pseudo code!

  // register to server -> client events
  _socketClient.someFunction = (data) => {
    store.dispatch({ type: "Server_Invoked_Some_Function", data });
  }

  _socketClient.someFunction2 = (data) => {
    store.dispatch({ type: "Server_Invoked_Some_Function2", data });
  }

  // connect to the server via the web-socket client API
  _socketClient.connect(() => callback());
}

We can use it in our index.js file:

let store = createStore(
  // your reducers here...
  // ...
  applyMiddleware(socketMiddleware) // our web socket middleware
)

// the callback will invoked only if the connection was successful
// the React-Dom's render function is our callback in this case
socketStart(store, () => { render(<App />, document.getElementById("root")); });
like image 72
Sagiv b.g Avatar answered Oct 02 '22 07:10

Sagiv b.g


With middleware, you can easily unfold/relay messages between Redux and Web Socket. Also, you can use Redux middleware without React, that means you can write API using Redux on server-side code (probably with Redux saga).

I would agree lifetime management as a React component is easier than a Redux middleware. But if you want to reconnect (destroy/recreate), you will need to use key props to make the reconciler to consider it as a new object, which is a little bit weird.

You can look at redux-websocket-bridge, which unfold Web Socket messages into Redux actions, and relay Redux actions to Web Socket.

On your Web Socket server, you send an action:

ws.on('connection', conn => {
  conn.send(JSON.stringify({ 
    type: 'GREETING', 
    payload: { now: Date.now() } 
  }));
});

You will get the GREETING action on your Redux reducer. And vice versa, when you want to relay an action to Web Socket, you mark your action with meta.send with true:

this.props.dispatch({ 
  type: 'SIGN_IN',
  meta: { send: true },
  payload: { authToken: '...' }
});
like image 36
Compulim Avatar answered Oct 02 '22 09:10

Compulim