当前位置: 首页 > 面试题库 >

使用socket.io的React-Redux和Websockets

唐高卓
2023-03-14
问题内容

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

您如何看待这种做法,这是一种更好的实施方法吗?


问题答案:

Spoiler: 我目前正在开发一个开源聊天应用程序。

通过将操作与中间件分离,甚至将套接字客户端与中间件分离,您可以做得更好。因此,结果如下:

  • 类型 ->每个请求的REQUEST,SUCCESS,FAILURE类型(不是必需的)。
  • 减速器 ->存储不同状态
  • 动作 ->发送动作以连接/断开连接/发出/监听。
  • 中间件 ->处理您的操作,并将当前操作传递或不传递给套接字客户端
  • 客户端 ->套接字客户端(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),
      }
    }

注意,套接字是一个参数化函数,通过这种方式,我们可以在整个应用程序中共享相同的套接字实例,而不必担心任何导入(稍后将说明如何执行此操作)。

中间件(socketMiddleware.js):

我们将使用与erikras / react-redux-universal-hot-
example示例
类似的策略,但是将它用于套接字而不是AJAX。

我们的套接字中间件将仅负责处理套接字请求。

中间件将操作传递到套接字客户端,并调度:

  • REQUEST(操作types[0]):正在请求(action.type发送到减速器)。
  • SUCCESS(操作types[1]):在请求成功时(action.type服务器响应action.result发送到reducer)。
  • 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-client的程序。

[可选](请参阅代码下面的1)。
关于so​​cket.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: 您也可以通过AJAX API调用遵循此策略。



 类似资料:
  • 问题内容: 我有一个简单的以React- Redux为动力的表格。我希望有一个form.container.tsx和form.component.tsx,其中form.container.tsx保留了到redux状态减去Field的所有连接。我试图将我的容器包装在react- redux的connect中,然后将reduxForm包装在其中,使其看起来像TypeScript,redux- form

  • 我是React Native的新手,我遇到的第一个障碍是在访问令牌到期时刷新我的访问令牌。基本实现是这样的,有一个访问令牌和一个刷新令牌。访问令牌到期,刷新令牌在授权标头中发送以刷新访问令牌。基于这篇SO帖子,我实现了一个中间件,它在每次API调用期间检查我们的访问令牌是否即将到期。如果是这样,它会启动一个动作创建者,该动作创建者会调用API来刷新和确认新的访问令牌。我在这里只展示了刷新调用,以免

  • 和 Flux 类似,Redux 也是需要注册一个回调函数 store.subscribe(listener) 来获取 State 的更新,然后我们要在 listener 里面调用 setState() 来更新 React 组件。 Redux 官方提供了 react-redux 来简化 React 和 Redux 之间的绑定,不再需要像 Flux 那样手动注册/解绑回调函数。 接下来看一下是怎么做到

  • 目录 为何组件没有被重新渲染、或者 mapStateToProps 没有运行? 为何组件频繁的重新渲染? 怎样使 mapStateToProps 执行更快? 为何不在被连接的组件中使用 this.props.dispatch ? 应该只连接到顶层组件吗,或者可以在组件树中连接到不同组件吗? React Redux 为何组件没有被重新渲染、或者 mapStateToProps 没有运行? 目前来看,

  • 问题内容: 我正在尝试了解react-redux的connect方法及其作为参数的功能。特别是。 以我的理解,它的返回值将是一个从状态派生的对象(因为它存在于商店中),其键将作为道具传递给目标组件(应用了connect的组件)。 这意味着目标组件所消耗的状态与存储在商店中的状态可能具有截然不同的结构。 问:可以吗? 问:这是预期的吗? 问:这是反模式吗? 问题答案: 问: 答:是的 问: 是的,这

  • 问题内容: 我开始使用React JS开发一个Web应用程序。我从主题森林购买了一个主题。在主题中,他们在组件中使用像这样的撰写。 如您所见,导出组件时,他们的代码最后使用了compose。我无法修改其构建结构。我现在想做的是我也喜欢使用react的connect功能。 通常connect用于代替撰写。现在,如果我想使用connect来处理应用程序的状态,如何将其与compose一起使用? 问题答