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?
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.
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 .
Redux thunk is the most popular middleware that allows you to call action creators, which then returns a function instead of an action object.
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.
There are couple of pros with placing such logic in middle-wares instead of actual components.
The main reasons in my opinion are:
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.redux
flow.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")); });
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: '...' }
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With