在 Redux Saga 中重用 websocket 连接对象

Posted

技术标签:

【中文标题】在 Redux Saga 中重用 websocket 连接对象【英文标题】:Reusing websocket connection object in Redux Saga 【发布时间】:2021-09-16 04:57:18 【问题描述】:

我有问题。我在 React Redux eventChannel 中的 websocket 的帮助下连接到服务器。我从服务器接收事件没有问题,但我无法将事件发送到服务器。我知道,我需要获取我在 eventChannel 中创建的 websocket 连接对象,以从反应组件发送事件。

export function* wsWatcher() 
    yield takeEvery("LOGIN_SUCCESS", wsConnectionWorker);


function* wsConnectionWorker() 
    const channel: EventChannel<boolean> = yield call(initWebsocket);
    while (true) 
        const action: Action<any> = yield take(channel);
        yield put(action);
    


function initWebsocket(): EventChannel<any> 
    return eventChannel((emitter) => 

    let id: string | undefined;
    let intervalId: number;

    const initConnection = () => 
        const connectionUrl: string = "ws://localhost:4000" 
            
        let ws = new WebSocket(connectionUrl);
        
        ws.onopen = () => 
            id && ws.send(JSON.stringify( type: "GET_USER_ID", id ));

            intervalId = window.setInterval(() => 
                ws.send(JSON.stringify( type: "PING" ))
            , 30000)
        ;

        ws.onclose = (e) => 
            console.log("Reconnect in 4s");
            intervalId && clearInterval(intervalId);
            id && setTimeout(initConnection, 4000); 
        ;
    ;

    initConnection();

    return () => 
        console.log("Socket off");
    ;
);

提前致谢

【问题讨论】:

【参考方案1】:

您可以在模块的范围内创建一个变量,并在第一次初始化后将连接对象存储在其中。以后需要连接时,只需使用此变量即可。

这是可行的,因为模块缓存在 nodejs 中。也就是说,模块中的代码仅在首次导入时运行,以后导入使用缓存的模块。

如需进一步参考,请阅读更多:Modules Caching

您也可以为此设置一个函数,例如此代码中的getConnection 函数。每当您需要 websocket 连接时,调用此函数,它只会创建一次连接。

export function* wsWatcher() 
    yield takeEvery("LOGIN_SUCCESS", wsConnectionWorker);


function* wsConnectionWorker() 
    const channel: EventChannel<boolean> = yield call(getConnection);
    while (true) 
        const action: Action<any> = yield take(channel);
        yield put(action);
    


// store the EventChannel after first init
let wsConnection: EventChannel<any> = null;

// create WS connection on first call
// reuse that connection in further calls
function getConnection(): EventChannel<any>
  if(!wsConnection)
    wsConnection = initWebsocket();
  return wsConnection;


function initWebsocket(): EventChannel<any> 
    return eventChannel((emitter) => 

    let id: string | undefined;
    let intervalId: number;

    const initConnection = () => 
        const connectionUrl: string = "ws://localhost:4000" 
            
        let ws = new WebSocket(connectionUrl);
        
        ws.onopen = () => 
            id && ws.send(JSON.stringify( type: "GET_USER_ID", id ));

            intervalId = window.setInterval(() => 
                ws.send(JSON.stringify( type: "PING" ))
            , 30000)
        ;

        ws.onclose = (e) => 
            console.log("Reconnect in 4s");
            intervalId && clearInterval(intervalId);
            id && setTimeout(initConnection, 4000); 
        ;
    ;

    initConnection();

    return () => 
        console.log("Socket off");
    ;
);

【讨论】:

嗨!感谢您的回答。但我很困惑。当我导入 wsConnection 时,我有一个具有以下属性的对象:take、flush 和 close。我认为,它的 redux 传奇属性。我可以保存一个“ws”对象吗,我在这里得到:“let ws = new WebSocket(connectionUrl);”? 你可以。但是,如果您使用缓存的 WebSocket,则在向其中添加 onopenonclose 事件侦听器时应该小心,因为这些事件侦听器可能已经存在。 所以我创建了一个全局变量,将 ws 变量保存到它并导出它?然后我可以导入它并使用我想要的方式? 是的,创建一个全局变量,最好也是一个函数getWs()getWs 应该返回已经可用的实例或创建一个并返回。在这种情况下,您将不得不更改 onopenonclose 逻辑。如果你能分享你想通过这个传奇实现的目标,我会修改我的代码。 我创建了一个变量,并在“let ws = new WebSocket()”字符串之后将 ws 对象保存到其中。例如,我现在可以导入它并在我的点击事件中使用它。我想要的是从我的反应组件访问 ws 以便将事件发送到服务器。创建全局变量有一点问题。如果我在组件内部使用这个变量(不在事件处理程序或其他东西中),它会说它仍处于连接状态。但在处理程序中一切都很好

以上是关于在 Redux Saga 中重用 websocket 连接对象的主要内容,如果未能解决你的问题,请参考以下文章

Redux-saga

在 redux-saga 中传递数据以进行注册的问题

无法弄清楚如何使用 redux-saga-test-plan 测试 redux-saga 功能

如何在 saga 文件中获取 redux 状态

Redux Saga:使用 redux-saga-test-plan 和 jest 在我的 saga 中测试纯 javascript 函数

手写Redux-Saga源码