React/Redux 基于客户端 Socket.IO 响应的全局状态管理?
React/Redux global state management based on Socket.IO responses on client side?
我有一个具有多种功能的网络应用程序,例如私人消息传递、购买、优惠等。我想让它实时工作,所以我决定使用 socket.io。我使用 redux 进行全局状态管理,但我不知道如何将它与 socket.IO 结合起来。这是我的想法:
1.Creating 用于套接字处理的文件,带有导出函数到 App.js 以创建套接字连接,发送和侦听不同的数据。
2.Whenever 我收到了相关信息,例如通知或购买请求 我更新了我的 redux 状态。
3.Finally 在我的组件中,我将对这些全局 redux 状态使用 useEffect,如果它发生变化,我将根据更改后的状态重新渲染我的组件。
这是一个好方法吗?如果不是,哪种是根据套接字接收到的信息全局管理我的组件的正确方法?
一般来说,根据您的需要,我认为这种方法没有任何问题。我将在这里提供一个可操作的示例。我的示例将采用 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
包自动捕获您的状态更改,触发必要元素的重新渲染。
简而言之,您的想法是正确的,我希望通过这个简短的可操作示例,您将能够完善适合您的解决方案。
我有一个具有多种功能的网络应用程序,例如私人消息传递、购买、优惠等。我想让它实时工作,所以我决定使用 socket.io。我使用 redux 进行全局状态管理,但我不知道如何将它与 socket.IO 结合起来。这是我的想法:
1.Creating 用于套接字处理的文件,带有导出函数到 App.js 以创建套接字连接,发送和侦听不同的数据。
2.Whenever 我收到了相关信息,例如通知或购买请求 我更新了我的 redux 状态。
3.Finally 在我的组件中,我将对这些全局 redux 状态使用 useEffect,如果它发生变化,我将根据更改后的状态重新渲染我的组件。
这是一个好方法吗?如果不是,哪种是根据套接字接收到的信息全局管理我的组件的正确方法?
一般来说,根据您的需要,我认为这种方法没有任何问题。我将在这里提供一个可操作的示例。我的示例将采用 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
包自动捕获您的状态更改,触发必要元素的重新渲染。
简而言之,您的想法是正确的,我希望通过这个简短的可操作示例,您将能够完善适合您的解决方案。