¿Qué es useReducer () Hook en React?

Introducción

La introducción de Hooks en React trajo una nueva forma de escribir y pensar sobre las aplicaciones React. Uno de los Hooks más populares entre los desarrolladores hasta ahora es useReducer, que nos permite manejar algunas manipulaciones y actualizaciones de estado complejas, y esto es lo que aprenderemos en este artículo.

En React hay dos ganchos principales que se utilizan para la gestión del estado, y todos los conocemos. Estos son:

Es posible que haya escuchado o usado React.useState hook, y si no, puede leer directamente la documentación oficial de React sobre Hooks aquí.

¿Alguna vez ha encontrado alguna dificultad para elegir una biblioteca de administración de estado para administrar y manejar un estado global en una aplicación React? O, ¿qué sucede si necesita administrar estructuras de datos más complicadas o realizar algún efecto secundario? Pensar en estos conceptos es bastante complicado y requiere mucho tiempo.

Bueno, esto es más o menos lo que vamos a aprender y cubrir en este artículo, ¡así que sigue leyendo!

Prerrequisitos

Para obtener todos los beneficios de este artículo, le recomiendo que vuelva a verificar esta lista de requisitos previos y configure el entorno como se sugiere.

  • Npm o hilo instalado
  • Versión de Node.js> = 12.x.x instalado
  • create-react-app cli instalado o usa npx
  • Fundamentos de Reaccionar ganchos, (si no, lea esto primero)

¿Qué es un reductor, de todos modos?

¿Qué es lo primero que te viene a la mente cuando escuchas el término reductor en React?

Para mí, siempre recuerdo la función Array.reducer () de JavaScript.

¡El único trabajo de un reductor es reducir!

Todos sabemos que el método reduce () original en JavaScript ejecuta una función dada para cada valor de una matriz considerando de izquierda a derecha. Y, aquí React.useReducer, de hecho, es como un mini reductor Redux.

Un reductor es básicamente una función que determina un cambio en el estado de una aplicación. Se necesita la ayuda de una acción usándola para recibir y determinar ese cambio en particular.

En react tenemos muchas opciones, como Redux, que pueden ayudar a administrar los cambios de estado de la aplicación en una sola tienda. Aquí aprenderemos cómo podemos hacer uso de un reductor para administrar el estado compartido en una aplicación.

Reducer es uno de los nuevos Hooks personalizados introducidos en React desde v16.8. Le permite actualizar partes del estado de su componente cuando se envían ciertas acciones, y es muy similar a cómo funciona Redux.

El reductor en “useReducer” proviene de Redux, que a su vez lo tomó prestado de Array.reduce () de JavaScript.

Lleva un estado inicial y una función reductora como argumentos y luego proporciona una variable de estado y una función de despacho para permitirnos actualizar el estado. Si está familiarizado con cómo Redux actualiza la tienda a través de reductores y acciones, entonces ya sabe cómo funciona useReducer. Y si no, también aprenderemos sobre la funcionalidad useReducer en core.

Entonces, básicamente, ¿qué es useReducer Hook?

El gancho useReducer se utiliza para manipulaciones de estado complejas y transiciones de estado. Al igual que los otros ganchos de React, podemos importar useReducer desde react como se muestra en el siguiente fragmento:

import React,  useReducer  from 'react';

React.useReducer es una función de gancho de React que acepta una función reductora y un estado inicial.

const [state, dispatch] = useReducer(reducer, initialState);

Esta función de gancho devuelve una matriz con 2 valores. El primero es el valor de estado, y el segundo valor es la función de envío que se utiliza además para desencadenar una acción con la ayuda de la desestructuración de la matriz.

En el contexto de React, así es como se ve un patrón useReducer típico:

JSX:

const reducer = function (currentState, action) // Crea un nuevo estado basado en el estado actual y la acción return newState const [state, dispatch] = useReducer (reducer, initialValue) // ejemplo de uso: dispatch (type: “SOMETHING_HAPPENED”) // O con “datos” opcionales: dispatch (type: “SOMETHING_HAPPENED”, data: newData)

Nota: El “estado” puede ser de cualquier tipo. No tiene por qué ser un objeto siempre. Podría ser un número, una matriz o cualquier otra cosa.

¿Muy genial? Ahora avancemos más y entendamos cómo usar useReducer.

¿Cómo utilizar el gancho useReducer?

Anteriormente vimos el useReducer básico que toma dos argumentos: estado inicial y una función reductora. Pero useReducer hook realmente toma un argumento más incluyendo reducer y estado inicial que es >>> una función para cargar el estado inicial de forma perezosa.

Esto suele ser útil cuando queremos que el estado inicial sea diferente dependiendo de alguna situación y en lugar de usar nuestro estado actual, podríamos crear el estado inicial en cualquier lugar, quizás de forma dinámica, y anulará el estado inicial.

La sintaxis del tercer argumento es:

const [state, dispatch] = useReducer(reducer, initialArgs, init); 

Ahora comprendamos paso a paso cómo usar useReducer y comprendamos lo que sucede detrás de la plataforma.

Considere el siguiente ejemplo clásico de código para ver el uso práctico de useReducer:

// Let’s begin to define the initial state of the component's state

const initialState =  count: 0  // Here is a function will determine how the state is updated

function reducer(state, action)    switch(action.type)  case 'INCREMENT':  return  count: state.count + 1  case 'DECREMENT': return  count: state.count - 1  case 'REPLACE': return  count: action.newCount  case 'RESET': return  count: 0  default: return state   // Now inside our component, we can initialize the state like below
const [state, dispatch] = useReducer(reducer, initialState);

Explicación:
En el fragmento de código anterior, tenemos

  • primero definió un estado inicial para nuestro componente
  • agregó una función reductora que actualiza ese estado según la acción enviada y,
  • finalmente hemos inicializado el estado de nuestro componente.

Ahora, como se prometió anteriormente, no es necesario comprender Redux para comprender este concepto. Así que analicemos todo y veamos qué está sucediendo.

La variable initialState

Este es el valor predeterminado del estado de nuestro componente cuando se monta por primera vez dentro de la aplicación.

La función reductora

El siguiente paso es actualizar el estado del componente cuando ocurren algunas acciones. Esta función indica qué debe contener el estado según la acción. Devuelve un objeto, que luego se usa para reemplazar / cambiar el estado.

Toma dos argumentos que son un estado y una acción; donde el estado no es más que el estado actual de su aplicación, y la acción es un objeto que contiene los detalles de todas las acciones que están sucediendo actualmente.

Normalmente, una acción puede verse así:

const replaceAction =  type: 'REPLACE', newCount: 10, 

Suele contener un tipo: que denota qué tipo de acción es. Una acción también puede contener más de un dato, que también puede ser el nuevo valor a actualizar en el estado.

Despachar una acción

Ahora, después de comprender el reductor y cómo determina el siguiente estado de nuestro componente a través de acciones, veamos cómo se distribuyen las acciones.

Dispatch es como una función que podemos pasar a otros componentes a través de accesorios.

Debes haber notado que useReducer devuelve dos valores en una matriz. El primero es el objeto de estado y el segundo es una función llamada despacho. Esto es lo que se usa para enviar una acción.

Por ejemplo, si queremos enviar replaceAction definido en el ejemplo anterior, haríamos algo como esto:

dispatch(replaceAction) // or dispatch( type: 'REPLACE', newCount: 10, )

Resumiendo un poco aquí, por lo tanto, para usar useReducer necesitamos lo siguiente:

  • Definiendo un estado inicial
  • Proporcionar una función que contiene acciones que pueden actualizar el estado.
  • Activar useReducer para enviar el estado actualizado

Comprensión de useReducer con ejemplos

Juguemos con algunos ejemplos reales ahora para comprender mejor el concepto:

Ejemplo: 1 ejemplo de contador clásico simple

Imaginemos que nuestro componente es un contador. useReducer aquí en el código siguiente acepta como argumentos una función reductora y un valor de estado inicial.

const Counter = () =>  const [state, dispatch] = useReducer(reducer, 0)

En este caso, nuestro estado es un número entero, que comienza en 0:

El reductor es una función que toma el estado actual y una acción, que puede ser un valor de cualquier tipo que desee. En este ejemplo, es una cadena:

const reducer = (state, action) =>  switch (action)  case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: throw new Error()  

Podemos usar JSX para hacer que este componente simple funcione de la siguiente manera:

const Counter = () => const [count, dispatch] = useReducer(reducer, 0) return ( <> Counter: count    )  

Este estado puede ser un objeto con ‘n’ número de propiedades, pero diferentes acciones solo cambian una propiedad a la vez.

Poniendo todo esto junto, nuestra Babel se verá así:

const  useReducer  = React const reducer = (state, action) =>  switch (action)  case 'INCREMENT': return state + 1 case 'DECREMENT': return state - 1 default: throw new Error()   const Counter = () =>  const [count, dispatch] = useReducer(reducer, 0) return ( <> Counter: count     )  ReactDOM.render(, document.getElementById('app')) 

Debería obtener el siguiente resultado:

Example2: Veamos otro ejemplo de TODO. Para mostrar la lista de elementos:

No era posible cambiar el estado de un elemento con la función de controlador antes. Pero sin embargo, ahora podemos hacerlo, por ejemplo, si necesitamos hacer que la lista de elementos tenga estado, podemos hacerlo usándolos como estado inicial para nuestro gancho useReducer definiendo la función reducer:

Considere el siguiente fragmento de JSX para reductor:

import React from 'react'; const initialTodo = [...]; const todoReducer = (state, action) => {  switch (action.type)  case 'DO_TODO':      return state.map(todo =>  if (todo.id === action.id)  return  ...todo, complete: true ;  else  return todo;  ); case 'UNDO_TODO': return state.map(todo =>  if (todo.id === action.id)  return  ...todo, complete: false ;  else  return todo;  ); default: return state;  }; const App = () =>  const [todos, dispatch] = React.useReducer( todoReducer, initialTodo ); const handleChange = () => ; return ( 
    todos.map(todo => (
  • ...
  • ))
); ; export default App;

Ahora podemos usar el controlador para enviar una acción para nuestra función reductora.

Debido a que necesitamos el id como el identificador de un elemento de todo para poder alternarlo, podemos pasar el elemento dentro de la función del controlador usando una función de flecha encapsulada como se muestra a continuación:

const handleChange = todo =>  dispatch( type: 'DO_TODO', id: todo.id ); ;

Y la entrada se verá así:

 handleChange(todo) />

Ahora implementemos para verificar nuestro controlador si un elemento de todo está completado o no por la siguiente condición:

const handleChange = todo =>  dispatch( type: todo.complete ? 'UNDO_TODO' : 'DO_TODO', id: todo.id, ); ;

Poniendo todo esto junto, nuestro componente se ve así:

import React from 'react'; const initialTodo = [  id: 'a', task: 'Do Something', complete: false,   ,  id: 'b', task: 'Walk over here', complete: false, , ]; const todoReducer = (state, action) => { switch (action.type)  case 'DO_TODO': return state.map(todo =>  if (todo.id === action.id)  return  ...todo, complete: true ;  else  return todo;  ); case 'UNDO_TODO': return state.map(todo =>  if (todo.id === action.id)  return  ...todo, complete: false ;  else  return todo;  ); default: return state;  }; const App = () =>  const [todos, dispatch] = React.useReducer( todoReducer, initialTodo ); const handleChange = todo =>  dispatch( type: todo.complete ? 'UNDO_TODO' : 'DO_TODO', id: todo.id, ); ; return ( 
    todos.map(todo => (
  • ))
); ; export default App;

Y aquí está la salida:

Ahora la pregunta más importante es:

¿Cuándo usamos React.useReducer en lugar de React.useState? La respuesta es muy simple e intentaré que sea aún más simple:

Podemos usar React.useReducer cuando —–

  • La arquitectura de su aplicación es compleja y de gran tamaño
  • Cuando la lógica para actualizar el estado es muy compleja o desea actualizar el estado en el fondo de su árbol de componentes
  • El valor del estado es un objeto o una matriz
  • Necesita una arquitectura de estado de la aplicación más predecible y fácil de mantener

Y podemos usar React.useState cuando —–

  • Tu aplicación es pequeña
  • El valor del estado es un valor primitivo
  • Transiciones de estado de UI simples
  • La lógica no es complicada y puede permanecer dentro de un componente

Conclusión

El gancho useReducer es una buena adición a la biblioteca React que permite una forma más simple, predecible y organizada de actualizar el estado de nuestro componente y hace que compartir datos entre componentes sea un poco más fácil.

Le permite optimizar el rendimiento de los componentes que desencadenan actualizaciones profundas porque ahora puede pasar fácilmente el envío en lugar de las devoluciones de llamada típicas.

E incluso si no toma nada más de este artículo, al menos debe recordar esto: que useReducer nos permite definir cómo actualizamos nuestro valor de estado.

¡Feliz codificación!

  • Add Your Comment