Introducción a la programación reactiva funcional en Redux

Comencemos por tener la idea básica de qué es la "Programación reactiva":

La programación reactiva es un paradigma de programación asincrónica que se ocupa de los flujos de datos y la propagación del cambio.

- Wikipedia

ReactiveX o Rx es la API más popular para la programación reactiva. Se basa en las ideologías del patrón observable, el patrón de iterador y la programación funcional. Rx tiene bibliotecas para diferentes idiomas, pero usaremos RxJS.

Rx se basa en Observables , Observers y Operators

Un observador esencialmente se suscribe a un observable.

El Observable luego emite flujos de datos que el Observador escucha y reacciona, poniendo en movimiento una cadena de operaciones en el flujo de datos. El poder real proviene de los operadores o "extensiones reactivas" (de ahí el término Rx) .

Los operadores le permiten transformar, combinar, manipular y trabajar con las secuencias de elementos emitidos por Observables.

Si no está familiarizado con Rx, es posible que tenga dificultades para comprender y usar Redux-Observable. ¡Así que sugiero que primero se ensucie las manos con Rx!

Ahora vamos a usar RxJS con Redux.

Redux-Observable

Redux-Observable es un middleware basado en RxJS para Redux

Esto es lo que Redux Docs tiene que decir sobre el middleware en Redux:

Middleware proporciona un punto de extensión de terceros entre el envío de una acción y el momento en que llega al reductor.

El middleware de Redux se puede utilizar para el registro, informes de fallas, hablar con una API asincrónica, enrutamiento y más. O podemos decir efectos secundarios en general.

Entonces, ¿cómo Redux-Observable hace todo eso?

A través de Epics. Las epopeyas son el núcleo primitivo de Redux-Observable. Una epopeya es simplemente una función simple que toma una acción y luego devuelve otra acción. Acción en → Acción fuera . Por tanto, las acciones se tratan como corrientes.

Cada acción enviada en cualquier componente de React pasará a través de tales funciones (Epics) como un flujo.

Veamos cómo se ve un Epic simple que toma action'PING’y devuelve uno nuevoaction'PONG’ :

const pingEpic = action$ => action$.filter(action => action.type === 'PING') .mapTo({ type: 'PONG' })

El $después actionse usa para indicar que estas variables hacen referencia a flujos. Así que tenemos un flujo de acciones que se pasan al Epic en el que hemos utilizado el filteroperador de RxJS.

Este operador de filtro filtra todas las acciones que no pertenecen a typePING! Por lo tanto, el Epic pingEpicsolo se ocupa de manejar las acciones del type‘PING’. Finalmente, esto action‘PING’se asigna a una nueva actionde las type‘PONG’satisfactorias reglas principales de Epics: Action In → Action Out .

Dado que cada epopeya solo se ocupa de un tipo específico de acción, tenemos un operador especial para action$(transmisión) para filtrar las acciones no deseadas de la transmisión. Este operador es el ofType()operador.

Reescribiendo el anterior Epic usando ofTypeobtenemos:

const pingEpic = action$ => action$.ofType('PING') .mapTo({ type: 'PONG' })

Si desea que su épica para permitir que más de un tipo de acción, el ofType()operador puede tomar cualquier número de argumentos, así: ofType(type1, type2, type3,...).

Introducción a los aspectos específicos de cómo funcionan las epopeyas

Puede pensar que la acción 'PING' simplemente entra y se deja consumir por esta epopeya. Ese no es el caso. Hay dos cosas para recordar siempre:

  1. Cada acción siempre va primero al reductor
  2. Solo después de eso es esa acción recibida por la épica

Por lo tanto, el ciclo de Redux funciona normalmente como debería.

El action‘PING’primero llega al reductor y luego el Epic lo recibe, luego se cambia a uno nuevo action‘PONG’que se envía al reductor.

¡Incluso podemos acceder al estado de la tienda dentro de un Epic porque el segundo argumento de un Epic es una versión ligera de Redux Store! Vea abajo:

const myEpic = (action$, store) =>

Podemos simplemente ca ll store.getState () y acceder al estado dentro de Epics.

Encadenamiento de operadores

Entre recibir una acción y enviar una nueva, podemos hacer todo tipo de efectos secundarios asincrónicos que queramos, como llamadas AJAX, sockets web, temporizadores, etc. Esto se hace utilizando los numerosos operadores proporcionados por Rx.

Estos operadores Rx le permiten componer secuencias asincrónicas juntas de una manera declarativa con todos los beneficios de eficiencia de las devoluciones de llamada, pero sin los inconvenientes de los manejadores de devolución de llamada anidados que normalmente se asocian con los sistemas asincrónicos.

Obtenemos los beneficios de las devoluciones de llamada, sin ese notorio 'infierno de devolución de llamada'.

Vea cómo podemos aprovechar el poder de los operadores a continuación.

Un caso de uso común

Supongamos que queremos buscar una palabra con algo así como una API de diccionario utilizando texto ingresado por el usuario en tiempo real. Básicamente, nos ocupamos de almacenar (en la tienda Redux) y mostrar los resultados de la llamada a la API. También nos gustaría eliminar el rebote de la llamada a la API para que la API se llame dentro de, digamos, 1 segundo después de que el usuario deje de escribir.

Así es como se hará usando los operadores Epic y RxJS:

const search = (action$, store) => action$.ofType('SEARCH') .debounceTime(1000) .mergeMap(action => ajax.getJSON(`//someapi/words/${action.payload}`) .map(payload => ({ type: 'SET_RESULTS', payload })) .catch(payload => Observable.of({type: 'API_ERROR', payload})) )

¡¿Mucho para soportar?! No se preocupe, analicemos eso.

La epopeya está consiguiendo un torrente de acciones todas oftype‘SEARCH’. Dado que el usuario escribe continuamente, la carga útil de cada acción entrante ( action.payload) contiene la cadena de búsqueda actualizada.

The operator debounceTime() is used to filter out some of the actions in the stream except the last one. It basically passes an action through it only if 1 second has elapsed without it receiving another action or observable.

We then make the AJAX request, mapping the results to another action 'set_RESULTS' which takes the response data (payload) to the reducer, which is the Action Out part.

Any API errors are caught using the catch operator. A new action is emitted with the error details and later displays a toaster with the error message.

Notice how the catch is inside the mergeMap() and after the AJAX request? This is because the mergeMap() creates a chain that is isolated. Otherwise the error would reach ofType() and will terminate our Epic. If that happens, the Epic will stop listening to any action in the future!

We can use traditional promises for AJAX requests as well. However, they have this inherent problem of not being able to get cancelled. So another important use case for using Epics is AJAX cancellation.

We use the takeUntil operator to handle this issue. This is done just like we used that catch operator inside mergeMap and after the AJAX request.

This is because takeUntil must stop the current AJAX request and not the entire Epic! Therefore, isolating operator chains is important here as well.

Debouncing, throttling, filtering, AJAX cancellation and others, are just the tip of the iceberg. We have a myriad of operators at our disposal, making difficult use-cases trivial to solve. Using these operators, you can get as creative as your imagination allows you to be! Functional Reactive Programming (FRP) is elegant in its own way.

My focus for this article was on the explanation part of FRP in Redux using Redux-Observable. For setting up Redux-Observable in React+Redux, refer to the official docs — its very well documented, detailed, and easy-breezy.

Be sure to check out my other article on Redux which explores the best practice for creating reducers:

Reducing the Reducer Boilerplate With createReducer()

First, a quick recap of what reducers in Redux are:medium.freecodecamp.org

Want to improve your JavaScript basics? Give these a read:

JavaScript ES6 Functions: The Good Parts

ES6 offers some cool new functional features that make programming in JavaScript much more flexible. Let’s talk about…medium.freecodecamp.orgA guide to JavaScript variable hoisting ? with let and const

New JavaScript developers often have a hard time understanding the unique behaviour of variable/function hoisting.medium.freecodecamp.org Function Hoisting & Hoisting Interview Questions

This is a part 2 for my previous article on Variable Hoisting titled “A guide to JavaScript variable hoisting ? with…medium.freecodecamp.org

Peace ✌️