Стандартный способ переподключения к серверу webSocket в redux-Saga? - PullRequest
0 голосов
/ 20 сентября 2019

Я пытаюсь подключиться с моего реагирующего приложения к websocket серверу, используя redux-saga и хочу перехватить потерю соединения (ошибка сервера, перезагрузка), чтобы восстановить соединение, скажем, с интервалами в 4 секунды, пока соединение снова не вернется.Проблема заключается в том, что при повторном подключении к webSocket redux store больше не обновляется.

Я пытался использовать eventChannel redux-saga, как показано в следующем коде.К сожалению, не было или, по крайней мере, я не смог найти никакой документации, отвечающей на wsconnect в 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)
    ]);
}

UPDATE

Чтобы завершить принятый ответ @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_CHANNEL и STOP_CHANNEL действия можно вызывать в componentDidMount и componentWillUnmount жизненного цикла реагирующего компонента, соответственно.

1 Ответ

0 голосов
/ 20 сентября 2019

Причина, по которой это не работает, заключается в том, что ваш рекурсивный вызов createEventChannel не yield передается в промежуточное программное обеспечение саги redux-saga не имеет возможности узнать о последующих созданиях канала событий.Вместо этого вы захотите, чтобы ваша рекурсивная функция была определена в канале событий, см. Код ниже, поэтому есть только один 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();
        };
    });
}
...