2016-06-17 2 views
13

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

Я хочу реализовать одно приложение чата с сокетами (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> 

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

+1

В общем, этот тип вопроса не подходит для stackoverflow, но да, ваш подход выглядит разумным. Единственное, что скачет на меня как «странное», условно вызывает «next (action)». Обычно вы хотите вызвать следующее промежуточное программное обеспечение (диспетчер), например. чтобы он обрабатывал протоколирование или что-то еще, что у вас там есть. – WTK

+2

Это выглядит достаточно справедливо, но я бы порекомендовал вам взглянуть на промежуточное программное обеспечение redux-saga, которое позволит вам «предпринять» определенное действие и развернуть генератор для него. Оттуда вы можете настроить более продвинутую логику, такую ​​как несколько каналов, отказаться от подписки и т. Д. :) – horyd

+0

@WTK относительно вызова следующего промежуточного программного обеспечения, добавить ли я условие else для отправки следующему промежуточному программному обеспечению? – Ganbel

ответ

23

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

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

  • Типы -> REQUEST, УСПЕХ, типы FAILURE для каждого запроса (не обязательно).
  • Редуктор -> для хранения различных состояний
  • Действия -> отправить действия подключить/отключить/испускают/слушать.
  • Middleware -> лечить ваши действия, и передать или не текущее действие для сокета клиента
  • Client -> сокет клиента (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.

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

Промежуточное проходит действие на сокет клиента, и передает:

  • ЗАПРОС (действие types[0]): запрашивает (action.type посылается на редуктор).
  • УСПЕХ (действие 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-клиент.

[необязательно] (см. Ниже в коде). Одна очень интересная особенность socket.io заключается в том, что вы можете иметь message acknowledgements, что было бы типичными ответами при выполнении 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.

+0

Работает как очарование! Я пытался выяснить, как на самом деле получать сообщения обратно клиенту. Я собираюсь предположить, что вы предполагаете использовать функцию on (event, fun) для этого? –

+0

@ ViktorSarström Это клиент на самом деле. Вы можете настроить его в действии с помощью '(client) => client.on ('event', fun)' – zurfyx

+0

Управляйте, чтобы понять это! Благодаря! –

Смежные вопросы