Подтвердить что ты не робот

React-Redux и Websockets с socket.io

Я новичок в этой технологии React-Redux, и мне бы хотелось, чтобы ваша помощь была с некоторой реализацией.

Я хочу реализовать одно приложение чата с сокетами (socket.io). Во-первых, пользователь должен зарегистрироваться (я использую паспорт на стороне сервера), а после того, как если регистрация завершена, пользователь должен подключиться к webSocket.

Я думал, что лучше всего использовать промежуточное программное обеспечение, такое как канал для всех действий, и в зависимости от того, какое действие получает промежуточное ПО, делать разные вещи.

Если тип действия AUTH_USER, создайте соединение клиент-сервер и настройте все события, которые будут поступать с сервера.

Если тип действия MESSAGE, отправьте на сервер сообщение.

Фрагменты кода:

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

Что вы думаете об этой практике, это лучшая реализация?

4b9b3361

Ответ 1

Спойлер: В настоящее время я разрабатываю то, что будет чат-приложением с открытым исходным кодом.

Вы можете сделать это лучше, отделив действия от промежуточного программного обеспечения и даже клиента сокета от промежуточного программного обеспечения. Следовательно, в результате чего-то вроде этого:

  • Типы → REQUEST, SUCCESS, FAILURE для каждого запроса (не обязательно).
  • Редуктор → для хранения различных состояний
  • Действия → отправлять действия для подключения/отключения/испускания/прослушивания.
  • Middleware → для обработки ваших действий и передачи или отсутствия текущего действия клиенту сокета
  • Клиент → клиент сокета (socket.io).

Код ниже берется из реального приложения, которое находится в стадии разработки (иногда слегка отредактировано), и их достаточно для большинства ситуаций, но некоторые вещи, такие как SocketClient, могут не быть на 100% завершены.

Действия

Вы хотите, чтобы действия были как можно более простыми, так как они часто повторяются, и вы, вероятно, закончите тем, что их много.

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

Обратите внимание, что сокет является параметризованной функцией, поэтому мы можем использовать один и тот же экземпляр сокета во всем приложении, и нам не нужно беспокоиться о каком-либо импорте (мы покажем, как это сделать позже).

Middleware (socketMiddleware.js):

Мы будем использовать аналогичную стратегию, как erikras/react-redux-universal-hot-example, хотя для сокета вместо AJAX.

Наше сокетное ПО будет отвечать за обработку только запросов сокетов.

Middleware передает действие клиенту сокета и отправляет:

  • REQUEST (действие types[0]): запрашивает (action.type отправляется на редуктор).
  • SUCCESS (действие types[1]): по запросу успешно (action.type и ответ сервера как action.result отправляется на редуктор).
  • FAILURE (действие types[2]): по запросу отказа (action.type и ответ сервера как action.error отправляются на редуктор).
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 заключается в том, что вы можете иметь подтверждения сообщений, которые будут типичными ответами при выполнении HTTP-запроса. Мы можем использовать их для проверки правильности каждого запроса. Обратите внимание: для того, чтобы использовать этот сервер функций, команды socket.io также должны иметь этот последний параметр подтверждения.

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

В нашем запуске приложения мы инициализируем SocketClient и передаем его в конфигурацию магазина.

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

configureStore.js

Мы добавляем socketMiddleware с нашей вновь инициализированной SocketClient к средним магазинам (помните тот параметр, который, как мы говорили, мы объясним позже?).

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

[Ничего особенного] Константы типов действий

Ничего особенного = то, что вы обычно делаете.

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

[Ничего особенного] Редуктор

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

Это может показаться большой работой, но как только вы ее настроите, это того стоит. Ваш соответствующий код будет легче читать, отлаживать, и вы будете менее склонны совершать ошибки.

PS: Вы также можете следовать этой стратегии с помощью вызовов API AJAX.