【问题标题】:React/Redux global state management based on Socket.IO responses on client side?基于客户端 Socket.IO 响应的 React/Redux 全局状态管理?
【发布时间】:2021-04-16 13:23:53
【问题描述】:

我有一个具有多种功能的网络应用程序,例如私人消息传递、购买、优惠等。我想让它实时工作,所以我决定使用 socket.io。我使用redux进行全局状态管理,但我不知道如何将它与socket.IO结合起来。这是我的想法:

1.使用导出函数到App.js创建一个用于套接字处理的文件,以创建一个套接字连接,发送和监听不同的数据。

2.每当我收到相关的信息时,例如通知或购买请求,我都会更新我的 redux 状态。

3.最后,在我的组件中,我将对这些全局 redux 状态使用 useEffect,如果它发生变化,我将根据我更改的状态重新渲染我的组件。

这是一个好方法吗?如果不是,哪种方法是根据套接字接收到的信息全局管理我的组件的正确方法?

【问题讨论】:

    标签: reactjs redux socket.io


    【解决方案1】:

    总的来说,根据您的需要,我认为这种方法没有任何问题。我将在这里提供一个可操作的示例。我的示例将假设 TypeScript,因为它比其他方式更容易转换为 JavaScript(如果您不使用 TypeScript)。

    关于您的第一个问题,我建议您在应用程序的任何地方都使用 Websocket 连接并将其作为上下文传递,并创建自定义挂钩以在任何地方使用该连接:

    import React, { createContext, FunctionComponent, ReactNode, useContext, useEffect, useMemo, useState } from 'react';
    import io from 'socket.io-client';
    
    export const WebsocketContext = createContext<SocketIOClient.Socket | null>(null);
    
    const WebsocketProvider: FunctionComponent<{ children: ReactNode }> = ({ children }: { children: ReactNode }) => {
      const [connection, setConnection] = useState<SocketIOClient.Socket | null>(null);
    
      const options: SocketIOClient.ConnectOpts = useMemo(() => ({}), []);
    
      useEffect(() => {
        try {
          const socketConnection = io(process.env.BASE_URL || '127.0.0.1', options);
          setConnection(socketConnection);
        } catch (err) {
          console.log(err);
        }
      }, [options]);
    
      return <WebsocketContext.Provider value={connection}>{children}</WebsocketContext.Provider>;
    };
    
    export const useWebsocket = (): SocketIOClient.Socket | null => {
      const ctx = useContext(WebsocketContext);
      if (ctx === undefined) {
        throw new Error('useWebsocket can only be used inside WebsocketContext');
      }
      return ctx;
    };
    
    export default WebsocketProvider;
    

    在上面我们创建了类型为SocketIOClient.Socket 并且默认为null 的上下文,因为当连接尚未准备好时,我们必须分配默认值。然后我们将 Websocket 提供程序创建为FunctionComponent,它接受子级并保持连接状态,useState 钩子最终返回提供 Websocket 连接的提供程序。我还提到SocketIOClient.ConnectOpts,根据您的需要,您可能希望提供连接选项;使用钩子时静态或动态。此外,useEffect 钩子会尝试建立连接或抛出错误。唯一会重新运行此钩子的依赖项是连接选项,以防它们动态更改。

    最后我们有了自定义钩子useWebsocket,我们可以将其导入任何组件并在上下文提供程序中使用。只需使用上下文提供程序包装您的根组件(或任何其他层次结构级别)以提供上下文,如下例所示:

    import React, { FunctionComponent } from 'react';
    import { BrowserRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
    import { v4 as uuid } from 'uuid';
    import routes from './App.routes';
    import WebsocketProvider from './websocket.context';
    
    const App: FunctionComponent = () => {
      return (
        <WebsocketProvider>
            <Router>
              <Switch>
                {routes.map((route) => (
                  <Route key={uuid()} {...route} />
                ))}
              </Switch>
              <Redirect to='/' />
            </Router>
        </WebsocketProvider>
      );
    };
    
    export default App;
    

    关于您的第二个问题,例如,您可以使用“useEffect”钩子在连接发出时做出反应并更新您的 Redux(或其他全局状态管理)存储。在这里,我还使用 Elvis 运算符来检查连接是否还没有准备好(如果它还没有准备好,因为null useEffect 钩子将在 socket 连接准备就绪时重新呈现):

    import React, { FunctionComponent, useEffect, useState } from 'react';
    import { useWebsocket } from './websocket.context';
    const Foo: FunctionComponent = () => {
      const dispatch = useDispatch();
      const socket = useWebsocket();
    
      useEffect(() => {
        socket?.on('myEmitEvent', (data: myEmitData) => {
          dispatch(myStoreAction(data));
        });
        return () => {
          socket?.off('myEmitEvent');
        };
      }, [socket, dispatch]);
    
      return ...
    };
    
    export default Foo;
    

    关于您的第三个问题,正如您提到的,您可以使用 useEffect 钩子或更简单的 useSelector 钩子来自 react-redux 包,它会自动捕获您的状态更改,触发必要时重新渲染元素。

    简而言之,您的想法恰到好处,我希望通过这个简短的可操作示例,您将能够改进适合您的解决方案。

    【讨论】:

    • 谢谢 :) 非常感谢您的有用回答
    • 我有问题createContext&lt;SocketIOClient.Socket | null&gt;(null); ...当我写这样的类型时,我得到一个错误Cannot find namespace 'SocketIOClient'.我应该以某种方式导入类型吗?
    • @ŽigaGrošelj 在不知道细节的情况下,我怀疑问题在于您使用的是哪个 socket.io-client 版本。在我的例子中,如果我没记错的话,版本是 2.4.0。但是,最新的是 4.2.0。因此,根据您使用的版本,该版本很可能会公开不同的类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-12-15
    • 2017-08-28
    • 2017-03-28
    • 2020-12-29
    • 2021-06-20
    • 1970-01-01
    • 2016-08-09
    相关资源
    最近更新 更多