React-Redux et Websockets avec socket.io
je suis nouveau avec cette technologie React-Redux et je voudrais votre aide avec une certaine mise en œuvre.
je veux implémenter une application de chat avec sockets (socket.io). Tout d'abord, l'utilisateur doit s'inscrire (j'utilise passport du côté du serveur) et après, si l'inscription est réussie, l'utilisateur doit se connecter au webSocket.
j'ai pensé que le meilleur sera d'utiliser un middleware comme un tuyau pour toutes les actions et selon quel type de action obtient le middleware, faire des choses différentes.
si le type d'action est AUTH_USER
, créez une connexion client-serveur et configurez tous les événements qui vont venir du serveur.
si le type d'action est MESSAGE
envoyer au serveur le message.
Extraits De Code:
----- 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>
Que pensez-vous de cette pratique, est-il une meilleure mise en œuvre?
1 réponses
Spoiler: je suis en train de développer ce qui va être une application de chat open-source.
vous pouvez faire mieux en séparant les actions de l'middleware, et même le client socket de l'middleware. Il en résulte quelque chose comme ceci:
- Types -> DEMANDE, de RÉUSSITE, d'ÉCHEC pour tous les types de demande (pas obligatoire).
- réducteur - > pour stocker des états différents
- Actions - > envoyer des actions pour se connecter / déconnecter / émettre / écouter.
- Middleware - > pour traiter vos actions, et passer ou non l'action courante au client socket
- Client - > socket client (socket.io).
le code ci-dessous est tiré de l'application réelle qui est en cours de développement (parfois légèrement modifié), et ils sont assez pour la majorité des situations, mais certaines choses comme le SocketClient pourrait ne pas être 100% complet.
Actions
vous voulez que les actions soient aussi simples que possible, car elles sont souvent répétées et vous finirez probablement par en avoir beaucoup.
export function send(chatId, content) {
const message = { chatId, content };
return {
type: 'socket',
types: [SEND, SEND_SUCCESS, SEND_FAIL],
promise: (socket) => socket.emit('SendMessage', message),
}
}
notez que socket est une fonction paramétrée, de cette façon nous pouvons partager la même instance de socket dans toute l'application et nous n'avons pas à nous soucier de n'importe quelle importation (nous verrons comment faire cela plus tard).
Middleware (socketMiddleware.js):
nous allons utiliser une stratégie similaire à erikras/react-redux-universal-hot-example utilise, mais pour socket au lieu D'AJAX.
Notre middleware socket sera responsable du traitement uniquement des requêtes socket.
Middleware passe l'action sur le client socket, et expédie:
- DEMANDE (action
types[0]
): demande (action.type
est envoyé à réducteur). - SUCCESS (action
types[1]
): sur demande succès (action.type
et réponse du serveur commeaction.result
est envoyé au réducteur). - défaillance (action
types[2]
): sur requête, défaillance (action.type
et réponse du serveuraction.error
sont envoyées au réducteur).
export default function socketMiddleware(socket) {
// Socket param is the client. We'll show how to set this up later.
return ({dispatch, getState}) => next => action => {
if (typeof action === 'function') {
return action(dispatch, getState);
}
/*
* Socket middleware usage.
* promise: (socket) => socket.emit('MESSAGE', 'hello world!')
* type: always 'socket'
* types: [REQUEST, SUCCESS, FAILURE]
*/
const { promise, type, types, ...rest } = action;
if (type !== 'socket' || !promise) {
// Move on! Not a socket request or a badly formed one.
return next(action);
}
const [REQUEST, SUCCESS, FAILURE] = types;
next({...rest, type: REQUEST});
return promise(socket)
.then((result) => {
return next({...rest, result, type: SUCCESS });
})
.catch((error) => {
return next({...rest, error, type: FAILURE });
})
};
}
SocketClient.js
le seul qui pourra jamais charger et gérer la prise.io-client.
[facultatif] (voir 1 ci-dessous dans le code). une caractéristique très intéressante sur le socket.io est le fait que vous pouvez avoir le message remerciements , qui seraient les réponses typiques lors d'une requête HTTP. Nous pouvons les utiliser pour vérifier que chaque demande est correcte. Notez que pour utiliser cette fonctionnalité de socket de serveur.les commandes io doivent aussi avoir ce dernier paramètre d'accusé de réception.
import io from 'socket.io-client';
// Example conf. You can move this to your config file.
const host = 'http://localhost:3000';
const socketPath = '/api/socket.io';
export default class socketAPI {
socket;
connect() {
this.socket = io.connect(host, { path: socketPath });
return new Promise((resolve, reject) => {
this.socket.on('connect', () => resolve());
this.socket.on('connect_error', (error) => reject(error));
});
}
disconnect() {
return new Promise((resolve) => {
this.socket.disconnect(() => {
this.socket = null;
resolve();
});
});
}
emit(event, data) {
return new Promise((resolve, reject) => {
if (!this.socket) return reject('No socket connection.');
return this.socket.emit(event, data, (response) => {
// Response is the optional callback that you can use with socket.io in every request. See 1 above.
if (response.error) {
console.error(response.error);
return reject(response.error);
}
return resolve();
});
});
}
on(event, fun) {
// No promise is needed here, but we're expecting one in the middleware.
return new Promise((resolve, reject) => {
if (!this.socket) return reject('No socket connection.');
this.socket.on(event, fun);
resolve();
});
}
}
app.js
sur notre application start-up, nous initialisons le SocketClient
et le passons à la configuration du magasin.
const socketClient = new SocketClient();
const store = configureStore(initialState, socketClient, apiClient);
configureStore.js
nous ajoutons le socketMiddleware
avec notre nouveau initialisé SocketClient
à la boutique middlewares (rappelez-vous ce paramètre que nous vous avons dit que nous vous expliquerons plus tard?).
export default function configureStore(initialState, socketClient, apiClient) {
const loggerMiddleware = createLogger();
const middleware = [
...
socketMiddleware(socketClient),
...
];
[rien de spécial] types d'Action constantes
rien de spécial = ce que vous feriez normalement.
const SEND = 'redux/message/SEND';
const SEND_SUCCESS = 'redux/message/SEND_SUCCESS';
const SEND_FAIL = 'redux/message/SEND_FAIL';
[rien de spécial] réducteur
export default function reducer(state = {}, action = {}) {
switch(action.type) {
case SEND: {
return {
...state,
isSending: true,
};
}
default: {
return state;
}
}
}
Il pourrait sembler comme beaucoup de travail, mais une fois que vous avez installé, il vaut la peine. Votre code sera plus facile à lire, le débogage et vous serez moins enclin à faire des erreurs.
PS: vous pouvez suivre cette stratégie avec les appels API AJAX ainsi.