tutorial react example español reactjs ecmascript-6 redux redux-saga

reactjs - react - redux saga vs redux thunk



¿Puedo usar los generadores es6 de redux-saga como oyentes de mensajes para websockets o eventsource? (1)

Estoy tratando de que Redux-Saga trabaje con el oyente onmessage . No sé por qué lo que tengo no funciona.

Tengo la siguiente puesta a punto.

// sagas.js import { take, put } from ''redux-saga''; import {transactions} from "./actions"; function* foo (txs) { console.log("yielding"); // appears in console yield put(transactions(txs)); // action *is not* dispatched console.log("yielded"); //appears in console } const onMessage = (event) => { const txs = JSON.parse(event.data); const iter = foo(txs); iter.next(); // do I really need to do this? }; function* getTransactions() { while(yield take(''APP_LOADED'')) { const stream = new EventSource(eventSourceUrl); stream.onopen = onOpen; stream.onmessage = onMessage; stream.onerror = onError; // this is just testing that `yield put` works yield put(transactions([{baz : 42}])); //this action *is* dispatched } };

Cuando se APP_LOADED acción getTransactions se llama a getTransactions , se abre la secuencia y se llama al oyente onMessage cuando se reciben datos del servidor, pero no tengo suerte en enviar la acción cuando se llama a la yield put(transactions(txs)) en el generador foo .

¿Alguien puede decirme qué estoy haciendo mal?


Una Saga puede invocarse solo desde dentro de otra Saga (usando el yield foo() o la yield call(foo) ).

En su ejemplo, se llama a la saga foo desde dentro de una función normal ( onMessage llamada onMessage ) por lo que simplemente devolverá el objeto iterador. Al generar un iterador (o una llamada a un generador) desde una Saga, permitimos que el middleware redux-saga intercepte esa llamada y ejecute el iterador para resolver todos los efectos generados. Pero en su código, stream.onmessage = onMessage simplemente haga una asignación simple para que el middleware no note nada.

En cuanto a la pregunta principal. Sagas típicamente toma eventos de la tienda Redux. Puede usar runSaga para conectar una saga a una fuente de entrada / salida personalizada, pero no será trivial aplicarla al caso de uso anterior. Así que propondré otra alternativa usando simplemente el efecto de call . Sin embargo, para poder introducirlo, tendremos que pasar de la perspectiva de empuje de los Eventos a una perspectiva de tirar .

La forma tradicional de manejar eventos es registrar algún detector de eventos en algún origen de eventos. Como asignar la devolución de llamada onMessage a stream.onmessage en el ejemplo anterior. Cada aparición de evento se empuja a la devolución de llamada del oyente. El origen del evento está en control total.

redux-saga adopta un modelo diferente: Sagas tira del Evento deseado. Como devoluciones de llamada, suelen hacer algún procesamiento. Pero tienen control total sobre qué hacer a continuación: pueden elegir tirar del mismo evento nuevamente, lo que imita el modelo de devolución de llamada, pero no están obligados a hacerlo. Pueden elegir tirar de otro Evento, comenzar otra Saga para tomar el relevo o incluso terminar su ejecución. Es decir, están en control de su propia lógica de progresión. Todo lo que puede hacer el origen del evento es resolver las consultas para futuros eventos.

Para integrar fuentes externas de inserción, necesitaremos transponer el Origen del evento del modelo de inserción al modelo de extracción; es decir, tendremos que crear un iterador de eventos desde el que podamos extraer los eventos futuros del origen del evento

Aquí hay un ejemplo de onmessage derivar un iterador onmessage de un EventSource

function createSource(url) { const source = new EventSource(url) let deferred source.onmessage = event => { if(deferred) { deferred.resolve(JSON.parse(event.data)) deferred = null } } return { nextMessage() { if(!deferred) { deferred = {} deferred.promise = new Promise(resolve => deferred.resolve = resolve) } return deferred.promise } } }

La función anterior devuelve un objeto con un método nextMessage que podemos usar para extraer los mensajes futuros. Llamarlo devolverá una Promesa que se resolverá con el siguiente mensaje entrante.

Tener la función de API createSource . Ahora podemos usarlo por un simple efecto de call

function* watchMessages(msgSource) { let txs = yield call(msgSource.nextMessage) while(txs) { yield put(transactions(txs)) txs = yield call(msgSource.nextMessage) } } function* getTransactionsOnLoad() { yield take(''APP_LOADED'') const msgSource = yield call(createSource, ''/myurl'') yield fork(watchMessages, msgSource) }

Puede encontrar una demostración en vivo del código anterior.

Una ventaja del enfoque anterior es que mantiene el código dentro de Sagas totalmente declarativo (usando solo las formas declarativas fork y call )