Reductores Asíncronos como Adaptadores
Extender reductores tradicionales para soportar lógica asíncrona
Ya hemos hablado del tema de Reductores como Acciones, donde entramos en detalle sobre el uso del reductor, para resolver tareas mediante acciones que completan un estado. La acciones son despachadas a una función encargada de computar el próximo estado, cuándo recibe la acción. Según la acción y sus parámetros, el estado podría volcar en otro. Pero, ¿Qué pasa cuando queremos despachar múltiples acciones a la vez, esperando entre ellas a llamadas asíncronas?
Esta vez, encontraremos una solución para estos casos, dónde el reductor será adaptado, para soportar despachar acciones asíncronas. De este modo, podremos formalizar lógica de negocio asíncrona, y blindar aún más nuestro código. Uno de los casos casos más comunes, será el consumo de un API, de tal forma que cada api soportada en el sistema, sea una interfaz completa y transparente usando reductores, cómo si se tratara de una receta.
El hook de reducción asíncrona — useReducerAsync
El hook useReducerAsync, tiene una naturaleza similar al useReducer tradicional, sin embargo, se distingue, porque asume que las acciones asíncronas, estarán bajo el modelo convenido de despachar acciones del tipo { type, ... }
, donde type
indicará el tipo de acción a despachar. El reductor asíncrono, en realidad, sólo extenderá la funcionalidad original del reductor principal, por lo que creará un reductor mezclado, entre el reductor original, y las adaptaciones asíncronas.
Sintaxis
import { useReducerAsync } from "use-reducer-async"
const [state, dispatch] = useReducerAsync(reductor, initialState, adapters)
Donde
adapters
es un objeto con cada clave como el tipo de acción adaptada, y cada función adaptada es de orden superior, recibiendo el despachador asociado en el orden superior y la acción despachada asíncronamente en el orden inferior, por ejemplo,{ FETCH_USER: ({ dispatch }) => async action => { ... }, ... }
.
Se observa que el hook no es estándar de react, sino más bien, viene del módulo use-reducer-async
que debemos incluir en las dependencias del proyecto, mediante el comando npm install use-reducer-async
o yarn add use-reducer-async
. También se asume, el dominio perfecto de useReducer, ya que state
, dispatch
, reductor
e initialState
son los esperados.
Construcción del Adaptador
const Adapters = {
ASYNC_ACTION_NAME: ({ dispatch }) => async action => {
TODO: Usa dispatch para despachar otras acciones
TODO: Usa action para acceder al payload y demás datos de la acción
}
... (define más acciones asíncronas)
}
Donde
dispatch
es el despachador de acciones asociado al reductor asíncrono yaction
es la acción despachada asíncronamente, cuándo se llama adispatch({ type: "ASYNC_ACTION_NAME", ... }
.
Se piensa en los adaptadores, como funciones superiores, que despachan otras acciones de forma asíncrona, en lugar de reducir mediante condiciones en una función reductora. Para consumir las acciones, se despacha una acción tradicional, bajo la convención que type
será el nombre o de la acción reducida o un adaptador asíncrono. Por ejemplo, dispatch({ type: "TURN_OFF", payload: { ignoreWarnings: true } })
despacha la acción tradicional TURN_OFF
, y dispatch({ type: "UPLOAD_FILE", payload: { file } })
sube un archivo al servidor de forma asíncrona. En realidad, no se nota la diferencia, entre llamar una acción tradicional y una acción adaptada asíncronamente.
En el siguiente ejemplo, se consume el api del New York Times, para mostrar películas buscadas. Se observa el uso de un reductor, un adaptador y useReducerAsync. También se observa la organización de las diferentes partes que consume al api, en diversos archivos, agrupados bajo el nombre de la tarea a realizar.
Conclusiones
Se ha profundizado sobre el uso de reductores, esta vez para extender su funcionalidad y adaptarla para realizar acciones asíncronas. Los reductores tradicionales, no soportan tareas asíncronas, ya que su responsabilidad es llevar un estado a otro, mediante una acción. Sin embargo, mediante un adaptador, usando el hook useReducerAsync podemos crear acciones adaptadas a código asíncrono, que despachen otras acciones tradicionales, mientras esperan respuestas asíncronas, como en el caso de consumir un api. Esto permite formalizar la lógica asíncrona, y evitar que la interfaz de programación devuelta por el hook personalizado de consumo, cargue la lógica relacionada, ya que esta podría contener reglas de validación, cálculos de negocio, y demás.
Usar los adaptadores, requiere entender la convención de despacho de acciones y las funciones de orden superior, fuera de esto, podemos usarlos como simples recetas, para romper la complejidad. La receta es la siguiente.
Una tarea se descompone en acciones. Las acciones se reducen en un estado. Si las acciones son asíncronas, se adaptan. La tarea se procesa mediante una interfaz de programación devuelta por un hook personalizado.
Entonces, una tarea se compone de un hook personalizado, que devuelve la interfaz de programación, usada para resolver la tarea, de un reductor que reduce cada acción asociada para completar la tarea, y un adaptador que procesa las acciones que sean asíncronas. Lema “El hook se reduce y se adapta”.
Documentación
El hook de reducción asíncrona — useReducerAsync
Developer New York Times
New York Times — Movie Reviews API
https://developer.nytimes.com/docs/movie-reviews-api/1/overview
Reductores como acciones
Stackblitz oficial de Dragon Nomada