reactjs - 在 redux-Saga 中重新连接到 webSocket 服务器的标准方法?

标签 reactjs redux websocket redux-saga

我正在尝试使用 redux-saga 从我的 react App 连接到 websocket 服务器,并希望捕获连接丢失(服务器错误,重新启动)以便以 4 秒的间隔重新连接,直到连接再次恢复。问题是在重新连接到 webSocket 时 redux store 不再更新。

我尝试按照以下代码使用 redux-saga 的 eventChannel。不幸的是,没有或者至少我找不到任何文档来回答 ws reconnect in redux-saga

import {eventChannel} from 'redux-saga';
import {all, takeEvery, put, call, take, fork} from 'redux-saga/effects'
import {INITIALIZE_WS_CHANNEL} from "../../constants/ActionTypes"
import {updateMarketData} from "../actions"


function createEventChannel() {
    return eventChannel(emit => {

        //Subscribe to websocket
        const ws = new WebSocket('ws://localhost:9000/rates');

        ws.onopen = () => {
            console.log("Opening Websocket");
        };

        ws.onerror = error => {
            console.log("ERROR: ", error);
        };

        ws.onmessage = e => {
            return emit({data: JSON.parse(e.data)})
        };

        ws.onclose = e => {
            if (e.code === 1005) {
                console.log("WebSocket: closed");
            } else {
                console.log('Socket is closed Unexpectedly. Reconnect will be attempted in 4 second.', e.reason);
                setTimeout(() =>  {
                    createEventChannel();
                }, 4000);
            }
        };

        return () => {
            console.log("Closing Websocket");
            ws.close();
        };
    });
}

function * initializeWebSocketsChannel() {
    const channel = yield call(createEventChannel);
    while (true) {
        const {data} = yield take(channel);
        yield put(updateMarketData(data));
    }
}

export function * initWebSocket() {
    yield takeEvery(INITIALIZE_WS_CHANNEL, initializeWebSocketsChannel);
}

export default function* rootSaga() {
    yield all ([
        fork(initWebSocket)
    ]);
}

更新

为了完成@azundo 为寻找 websocket 和 redux-saga 完整示例的人所接受的答案,我添加了以下代码:

function * initializeWebSocketsChannel() {
    console.log("going to connect to WS")
    const channel = yield call(createEventChannel);
    while (true) {
        const {data} = yield take(channel);
        yield put(updateMarketData(data));
    }
}

export function * startStopChannel() {
    while (true) {
        yield take(START_CHANNEL);
        yield race({
            task: call(initializeWebSocketsChannel),
            cancel: take(STOP_CHANNEL),
        });
        //if cancel wins the race we can close socket
        ws.close();
    }
}

export default function* rootSaga() {
    yield all ([
        startStopChannel()
    ]);
}

START_CHANNELSTOP_CHANNEL 操作可以在 componentDidMountcomponentWillUnmount 分别是 React 组件生命周期。

最佳答案

这不起作用的原因是因为您对 createEventChannel 的递归调用没有被 yielded 到 saga 中间件 redux-saga无法知道后续事件 channel 的创建。您会希望在事件 channel 中定义递归函数,请参阅下面的代码,因此只有一个 eventChannel 始终挂接到商店中。

另请注意,在预期的套接字关闭时添加了发射 END,这样您就不会在不重新连接时让 eventChannel 永远打开。

import {eventChannel, END} from 'redux-saga';

let ws; //define it here so it's available in return function

function createEventChannel() {
    return eventChannel(emit => {
          function createWs() {
            //Subscribe to websocket
            ws = new WebSocket('ws://localhost:9000/rates');

            ws.onopen = () => {
                console.log("Opening Websocket");
            };

            ws.onerror = error => {
                console.log("ERROR: ", error);
            };

            ws.onmessage = e => {
                return emit({data: JSON.parse(e.data)})
            };

            ws.onclose = e => {
                if (e.code === 1005) {
                    console.log("WebSocket: closed");
                    // you probably want to end the channel in this case
                    emit(END);
                } else {
                    console.log('Socket is closed Unexpectedly. Reconnect will be attempted in 4 second.', e.reason);
                    setTimeout(() =>  {
                        createWs();
                    }, 4000);
                }
            };
        }
        createWs();

        return () => {
            console.log("Closing Websocket");
            ws.close();
        };
    });
}

关于reactjs - 在 redux-Saga 中重新连接到 webSocket 服务器的标准方法?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58026629/

相关文章:

javascript - Prop 仅在我刷新页面后呈现

javascript - 是否可以通过 JavaScript 中的 API 网关将文件对象传递给 AWS Lambda?

javascript - ES6 语法模式下 Redux 未定义

c# - 在 .NET 4.5 中为 websocket 握手设置自定义 header

javascript - 是否可以在不使用服务器的情况下在不同计算机(但在同一 WLAN 上)的两个浏览器之间发送数据?

reactjs - useRef react Hook 的确切行为是什么?是否在每次重新渲染时重新创建对象?

javascript - 尝试执行使用上下文传递给组件的函数

node.js - 在 React 中使用 Apollo.js 将查询映射到 props

reactjs - 在操作调用源自的 View 中处理 Redux Saga 结果

apache - 从 http 切换 https 后 Websocket 不工作