React-Redux i Websockets z socket.io

Jestem nowy w tej technologii React-Redux i proszę o pomoc przy implementacji.

Chcę zaimplementować jedną aplikację czatu z gniazdami (socket.io). najpierw użytkownik musi się zarejestrować (używam passport po stronie serwera), a następnie, jeśli Rejestracja się powiedzie, użytkownik musi połączyć się z webSocket.

Pomyślałem, że najlepiej będzie użyć middleware jak rury dla wszystkich działań i w zależności od tego, jakiego rodzaju działania dostaje middleware, zrobić różne rzeczy.

Jeśli typem akcji jest AUTH_USER, Utwórz połączenie klient-serwer i skonfiguruj wszystkie zdarzenia, które będą pochodzić z serwera.

Jeśli typem akcji jest MESSAGE Wyślij do serwera wiadomość.

Fragmenty Kodu:

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

}

------ Indeks.js -------

import {createStore, applyMiddleware} from 'redux';

import socketMiddleware from './socketMiddleware';



const createStoreWithMiddleware = applyMiddleware(

  socketMiddleware

)(createStore);

const store = createStoreWithMiddleware(reducer);

<Provider store={store}>

    <App />

</Provider>

Co sądzisz o tej praktyce, czy jest to lepsza realizacja?

Author: zurfyx, 2016-06-17

1 answers

Spoiler: obecnie rozwijam coś, co będzie aplikacją do czatu typu open-source.

Możesz to zrobić lepiej, oddzielając akcje od oprogramowania pośredniczącego, a nawet klienta gniazd od oprogramowania pośredniczącego. Dlatego, w wyniku czegoś takiego:

  • typy - > typy REQUEST, SUCCESS, FAILURE dla każdego żądania (nieobowiązkowe).
  • reduktor - > do przechowywania różnych stanów
  • akcje - > wyślij akcje do connect / disconnect / emit / listen.
  • Oprogramowanie pośredniczące -> do przetwarzania Twoich działań i przekazywania bieżącej akcji klientowi socket
  • Client -> socket client (socket.io).

poniższy kod pochodzi z prawdziwej aplikacji, która jest w fazie rozwoju (czasami lekko edytowane), i są one wystarczające dla większości sytuacji, ale niektóre rzeczy, takie jak SocketClient może nie być 100% kompletna.

Działania

Chcesz, aby działania były tak proste, jak to tylko możliwe, ponieważ często są powtarzane i prawdopodobnie będziesz mieć ich wiele.

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

Zauważ, że socket jest parametryzowaną funkcją, w ten sposób możemy współdzielić tę samą instancję socket w całej aplikacji i nie musimy się martwić o żaden import (pokażemy jak to zrobić później).

Middleware (socketmidd.js):

Użyjemy podobnej strategii jak erikras/react-redux-universal-hot-example , choć dla socket zamiast AJAX.

Nasze oprogramowanie pośredniczące gniazd będzie odpowiedzialne za przetwarzanie tylko żądań gniazd.

Middleware przekazuje akcję do klienta gniazda i wysyła:

  • REQUEST (action types[0]): is requesting (action.type jest wysyłane do reduktora).
  • SUCCESS (action types[1]): na życzenie success (action.type i odpowiedź serwera jako action.result jest wysyłana do reduktora).
  • FAILURE (action types[2]): na żądanie failure (action.type i odpowiedź serwera jako action.error są wysyłane do reduktora).
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

Socket.io-client jest jedynym, który kiedykolwiek ładuje i zarządza socket. io-client.]}

[nieobowiązkowe] (zob. 1 poniżej w kodzie). jedna bardzo ciekawa cecha o socket.io jest fakt, że można mieć potwierdzenia wiadomości , co byłoby typowym odpowiedzi podczas wykonywania żądania HTTP. Możemy ich użyć, aby zweryfikować, czy każde żądanie było poprawne. Należy pamiętać, że w celu skorzystania z tej funkcji serwer socket.io polecenia muszą również posiadać ten ostatni parametr potwierdzenia.

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

Podczas uruchamiania naszej aplikacji inicjujemy SocketClient i przekazujemy go do konfiguracji sklepu.

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

ConfigureStore.js

Dodajemy socketMiddleware z naszą nowo zainicjowaną SocketClient do sklepu middlewares (pamiętasz ten parametr, który powiedzieliśmy, że wyjaśnimy później?).

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

[nic specjalnego] stałe typów akcji

Nic specjalnego = to, co normalnie byś zrobił.

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

[nic specjalnego] reduktor

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

Może to wyglądać na dużo pracy, ale gdy już ją skonfigurujesz, warto. Twój odpowiedni kod będzie łatwiejszy do odczytania, debugowania i będziesz mniej podatny na błędy.

PS: ty może śledzić tę strategię z wywołaniami AJAX API, jak również.

 28
Author: zurfyx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-24 00:15:44