2016-06-17 14 views
13

Ich bin neu mit dieser Technologie React-Redux und ich möchte Ihre Hilfe mit einigen Implementierung.React-Redux und Websockets mit socket.io

Ich möchte eine Chat-Anwendung mit Sockets (socket.io) implementieren. Zuerst muss sich der Benutzer anmelden (ich benutze den Passport auf der Serverseite) und danach, wenn die Anmeldung erfolgreich ist, muss sich der Benutzer mit dem webSocket verbinden.

Ich dachte, dass das Beste ist, eine Middleware wie eine Pipe für alle Aktionen zu verwenden und abhängig davon, welche Art von Aktion die Middleware bekommt, verschiedene Dinge tun.

Wenn der Aktionstyp AUTH_USER ist, erstellen Sie eine Client-Server-Verbindung und richten Sie alle Ereignisse ein, die vom Server kommen.

Wenn der Aktionstyp MESSAGE ist, senden Sie die Nachricht an den Server.

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 -------

Was denken Sie über diese Praxis, ist es eine bessere Implementierung?

+1

Im Allgemeinen ist diese Art von Frage nicht gut für stackoverflow, aber ja, Ihr Ansatz sieht vernünftig aus. Nur das, was mich als "komisch" bezeichnet, ruft nur bedingt "next (action)" auf. Normalerweise möchten Sie die nächste Middleware (Dispatcher) z. um es mit der Protokollierung oder irgendetwas anderem, das Sie dort haben, umgehen zu können. – WTK

+2

Das sieht fair aus, aber ich würde wirklich einen Blick in die Redux-Saga-Middleware empfehlen, die es dir erlauben wird, eine bestimmte Aktion auszuführen und einen Generator dafür zu aktivieren. Von dort könnten Sie erweiterte Logik, wie mehrere Kanäle, Abbestellen, etc. einrichten. – horyd

+0

@WTK in Bezug auf die nächste Middleware, soll ich eine andere Bedingung hinzufügen, um an die nächste Middleware zu senden? – Ganbel

Antwort

23

Spoiler: Ich entwickle gerade, was eine Open-Source-Chat-Anwendung sein wird.

Sie können das besser tun, indem Sie Aktionen von der Middleware und sogar dem Socket-Client von der Middleware trennen. Daher, was in etwa wie folgt:

  • Typen -> REQUEST, Erfolg, Misserfolg Typen für jede Anforderung (nicht zwingend).
  • Reducer -> speichern verschiedene Zustände
  • Aktionen -> Aktionen senden verbinden/trennen/emittieren/hören.
  • Middleware -> Ihre Aktionen zu behandeln, und übergeben oder nicht die aktuelle Aktion an den Socket-Client
  • Client- -> Socket-Client (socket.io).

Der folgende Code aus der realen Anwendung genommen wird, die in der Entwicklung (manchmal leicht bearbeitet), und sie sind für die meisten Situationen genug, aber bestimmte Sachen wie das SocketClient vielleicht nicht 100% vollständig sein.

Aktionen

Sie wollen Aktionen so einfach wie möglich sein, da sie oft Arbeit wiederholt werden, und Sie werden wahrscheinlich viele von ihnen am Ende mit.

export function send(chatId, content) { 
    const message = { chatId, content }; 
    return { 
    type: 'socket', 
    types: [SEND, SEND_SUCCESS, SEND_FAIL], 
    promise: (socket) => socket.emit('SendMessage', message), 
    } 
} 

Beachten Sie, dass Buchse ist eine parametrisierte Funktion, so können wir die gleiche Buchse Instanz in der gesamten Anwendung teilen können und müssen wir uns über jede Einfuhr keine Sorge überhaupt (wir werden zeigen, wie dies später zu tun).

Middleware (socketMiddleware.js):

Wir werden eine ähnliche Strategie wie erikras/react-redux-universal-hot-example Anwendungen verwenden, obwohl für Sockel anstelle von AJAX.

Unsere Socket-Middleware wird nur für die Verarbeitung von Socket-Anforderungen zuständig sein.

Middleware passiert die Aktion auf den Socket-Client, und entsendet:

  • REQUEST (action types[0]): anfordert (action.type wird Minderer gesendet).
  • ERFOLG (Aktion types[1]): auf Anfrage Erfolg (action.type und Serverantwort als action.result wird an Reducer gesendet).
  • FAILURE (Aktion types[2]): auf Anfrage Fehler (action.type und Serverantwort als action.error werden zum Reducer geschickt).
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

Die einzige, die jemals geladen wird und die socket.io-Client verwalten.

[optional] (siehe 1 unten im Code). Ein sehr interessantes Feature über socket.io ist die Tatsache, dass Sie message acknowledgements haben können, was die typischen Antworten bei einer HTTP-Anfrage wären. Wir können sie verwenden, um zu überprüfen, ob jede Anfrage korrekt war. Beachten Sie, dass socket.io-Befehle auch diesen neuesten Bestätigungsparameter haben müssen, um diesen Feature-Server verwenden zu können.

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

Auf unserer App-Start-up, initialisieren wir die SocketClient und an die Speicherkonfiguration übergeben.

const socketClient = new SocketClient(); 
const store = configureStore(initialState, socketClient, apiClient); 

configureStore.js

Wir fügen den socketMiddleware mit unserem neu SocketClient den Speicher Middle initialisiert (denken Sie daran, dass die Parameter, die wir Ihnen gesagt, dass wir später erklären würde?).

export default function configureStore(initialState, socketClient, apiClient) { 
const loggerMiddleware = createLogger(); 
const middleware = [ 
    ... 
    socketMiddleware(socketClient), 
    ... 
]; 

[Nothing special] Aktionstypen Konstanten

Nothing special =, was Sie normalerweise tun würde.

const SEND = 'redux/message/SEND'; 
const SEND_SUCCESS = 'redux/message/SEND_SUCCESS'; 
const SEND_FAIL = 'redux/message/SEND_FAIL'; 

[Nothing special] Reducer

export default function reducer(state = {}, action = {}) { 
    switch(action.type) { 
    case SEND: { 
     return { 
     ...state, 
     isSending: true, 
     }; 
    } 
    default: { 
     return state; 
    } 
    } 
} 

Es mag wie eine Menge Arbeit suchen, aber wenn Sie es eingerichtet haben, es lohnt sich. Ihr relevanter Code wird einfacher zu lesen und zu debuggen sein und Sie werden weniger anfällig für Fehler sein.

PS: Sie können diese Strategie auch mit AJAX API-Aufrufen verfolgen.

+0

Funktioniert wie ein Charme! Ich habe versucht herauszufinden, wie Sie tatsächlich Nachrichten zurück zum Client bekommen. Ich gehe davon aus, dass Sie die on (event, fun) -Funktion dafür verwenden sollen? –

+0

@ ViktorSarström Das ist der Client tatsächlich "on". Sie können es in einer Aktion einrichten mit '(client) => client.on ('event', fun)' – zurfyx

+0

Verwalten Sie es herauszufinden! Vielen Dank! –