I'm new with that technology React-Redux and I would like your help with some implementation.
I want to implement one chat application with sockets (socket.io). First, the user has to sign up (I use passport in the server side) and after, if the sign up is successful the user has to connect to the webSocket.
I thought that the best will be to use a middleware like a pipe for all the actions and depending of what kind of action gets the middleware, do different things.
If the action type is AUTH_USER
, create client-server connection and set up all the events which will come from the server.
If the action type is MESSAGE
send to the server the message.
Code Snippets:
----- socketMiddleware.js ----
import { AUTH_USER, MESSAGE } from '../actions/types';
import * as actions from 'actions/socket-actions';
import io from 'socket.io-client';
const socket = null;
export default function ({ dispatch }) {
return next => action => {
if(action.type == AUTH_USER) {
socket = io.connect(`${location.host}`);
socket.on('message', data => {
store.dispatch(actions.addResponse(action.data));
});
}
else if(action.type == MESSAGE && socket) {
socket.emit('user-message', action.data);
return next(action)
} else {
return next(action)
}
}
}
------ index.js -------
import {createStore, applyMiddleware} from 'redux';
import socketMiddleware from './socketMiddleware';
const createStoreWithMiddleware = applyMiddleware(
socketMiddleware
)(createStore);
const store = createStoreWithMiddleware(reducer);
<Provider store={store}>
<App />
</Provider>
What do you think about that practise, is it a better implementation?
Spoiler: I am currently developing what's going to be an open-source chat application.
You can do that better by separating actions from the middleware, and even the socket client from the middleware. Hence, resulting in something like this:
The code below is taken from the real app which is under development (sometimes slightly edited), and they are enough for the majority of situations, but certain stuff like the SocketClient might not be 100% complete.
Actions
You want actions to be as simple as possible, since they are often repeated work and you'll probably end up having lots of them.
Notice that socket is a parametrized function, this way we can share the same socket instance throughout the whole application and we don't have to worry about any import whatsoever (we'll show how to do this later).
Middleware (socketMiddleware.js):
We'll use a similar strategy as erikras/react-redux-universal-hot-example uses, though for socket instead of AJAX.
Our socket middleware will be responsible for processing only socket requests.
Middleware passes the action onto the socket client, and dispatches:
types[0]
): is requesting (action.type
is sent to reducer).types[1]
): on request success (action.type
and server response asaction.result
is sent to reducer).types[2]
): on request failure (action.type
and server response asaction.error
are sent to reducer).SocketClient.js
The only one that will ever load and manage the socket.io-client.
[optional] (see 1 below in the code). One very interesting feature about socket.io is the fact that you can have message acknowledgements, which would be the typical replies when doing an HTTP request. We can use them to verify that each request was correct. Note that in order to make use of this feature server socket.io commands do also have to have this latest acknowledgement parameter.
app.js
On our app start-up, we initialize the
SocketClient
and pass it to the store configuration.configureStore.js
We add the
socketMiddleware
with our newly initializedSocketClient
to the store middlewares (remember that parameter which we told you we would explain later?).[Nothing special] Action types constants
Nothing special = what you would normally do.
[Nothing special] Reducer
It might look like a lot of work, but once you have set it up it is worth it. Your relevant code will be easier to read, debug and you will be less prone to make mistakes.
PS: You can follow this strategy with AJAX API calls as well.