hand Inicio
hand JSBloqs
hand GutenBloqs
Qu茅?
noched铆a

DESARROLLO WEB con
REACT y WORDPRESS

Ap煤ntate a la newsletter (escribo algo de tanto en cuanto)

Utilizando useReducer

500 palabras
2 minutos
July 3, 2020
blogjavascript

useReducer nos permite integrar una funci贸n en useState y as铆 poder trabajar m谩s c贸modamente con estados complejos

  1. useReducer
  2. Por qu茅 useReducer?
  3. A帽adiendo complejidad al estado

useReducer

Una manera de definir useReducer es diciendo que es la versi贸n completa de useState

Con useState podemos controlar el estado (es decir, guardar variables que persisten entre los distintos re-renders que ocurran en nuestra aplicaci贸n)

js
const [estado, cambiarEstado] = useState('')

La funci贸n useState acepta cualquier valor (excepto una funci贸n, pero vamos a olvidarnos de esto) que pasa a ser el valor inicial del estado

Y nos devuelve dos variables, el estado en s铆 y la funci贸n para canviar el estado

Ejemplos:

js
// inicializamos el hook
const [estado, setEstado] = useState({ status: 'pending', value: null })
// cambiamos el valor del estado
setEstado({ status: 'ok', value: 'whatever' })

Inciso 1

Cada vez que cambiamos el estado nuestra aplicaci贸n sufrir谩 un re-renderizado

Si queremos almacenar un estado sin forzar ese repintado, en lugar de useState utilizaremos el hook useRef

Inciso 2

Un error com煤n es pensar que se ha cambiado el estado sin que sea verdad

js
const [estado, setEstado] = useState({ status: 'pending', value: null })
estado.status = 'ok'
setEstado(estado) // no hay repaint!

Aqu铆 hemos cambiado el contenido del objeto pero NO el objeto, por lo que para React no hay raz贸n para repintar nada

Para cambiarlo tendr铆amos que cambiar el objeto

js
const [estado, setEstado] = useState({ status: 'pending', value: null })
setEstado({ ...estado, status: 'ok' })

Por qu茅 useReducer?

El nombre de useReduce ya nos da pistas de que esto tiene que ver con el m茅todo reduce()

[ tienes una entrada del Array.reduce aqu铆 ]

Vamos con c贸digo

Pensando en la cl谩sica aplicaci贸n ToDo, vamos a crear una lista de tareas que entraremos con un <input>

jsx
import React, { useRef, useState } from 'react'
export const App = () => {
// referencia para gestionar el input
const inputRef = useRef()
// el estado, con un valor inicial de []
const [elements, setElements] = useState([])
// funciones para a帽adir y quitar elementos del estado
const addElement = () => setElements([inputRef.current.value, ...elements])
const removeElement = i => setElements([...elements.slice(0, i), ...elements.slice(i + 1)])
// el jsx que gestiona la interfaz
return (
<div>
<input ref={inputRef} />
<button onClick={addElement}>鉁忥笍</button>
{elements.map((el, i) => (
<div key={'element' + i}>
<span>{el}</span>
<button onClick={() => removeElement(i)}>鉁旓笍</button>
</div>
))}
</div>
)
}
// el export default no es necesario si importamos `App` expl铆citamente (lo cual es recomendable)
// aqu铆 lo a帽ado aqu铆 para que el codesandbox funcione out-of-the-box
export default App
Ver en CodeSandBox

Aqu铆 tenemos un input para a帽adir entradas en la lista, y un bot贸n para quitar entradas de la lista, y esa lista la guardamos como estado

Y ese estado es un array

Esto as铆 es la mar de legible, pero voy a refactorizarlo para abstraer la l贸gica y tener un elemento en com煤n

jsx
import React, { useRef, useState } from 'react'
export const App = () => {
const inputRef = useRef()
const [elements, setElements] = useState([])
// mi super nueva funci贸n
const changeElements = action =>
action.type === 'add'
? [action.value, ...elements]
: action.type === 'remove'
? [...elements.slice(0, action.i), ...elements.slice(action.i + 1)]
: []
const addElement = () => setElements(changeElements({ type: 'add', value: inputRef.current.value }))
const removeElement = i => setElements(changeElements({ type: 'remove', i: i }))
return (
<div>
<input ref={inputRef} />
<button onClick={addElement}>鉁忥笍</button>
{elements.map((el, i) => (
<div key={'element' + i}>
<span>{el}</span>
<button onClick={() => removeElement(i)}>鉁旓笍</button>
</div>
))}
</div>
)
}
export default App
Ver en CodeSandBox

Ahora tengo una funci贸n que me gestiona la l贸gica changeElements y dos funciones para a帽adir y quitar elementos m谩s legibles

En general el c贸digo de ahora es m谩s ofuscado, pero se entiende el concepto

Bien

Pues ahora vamos a reescribir lo mismo con useReducer

jsx
import React, { useRef, useReducer } from 'react'
export const App = () => {
const inputRef = useRef()
const [elements, setElements] = useReducer(
(state, action) =>
action.type === 'add'
? [action.value, ...state]
: action.type === 'remove'
? [...state.slice(0, action.i), ...state.slice(action.i + 1)]
: [],
[]
)
const addElement = () => setElements({ type: 'add', value: inputRef.current.value })
const removeElement = i => setElements({ type: 'remove', i: i })
return (
<div>
<input ref={inputRef} />
<button onClick={addElement}>鉁忥笍</button>
{elements.map((el, i) => (
<div key={'element' + i}>
<span>{el}</span>
<button onClick={() => removeElement(i)}>鉁旓笍</button>
</div>
))}
</div>
)
}
export default App
Ver en CodeSandBox

Es decir, lo que nos permite useReducer es integrar la funci贸n que antes ten铆amos fuera, dentro de la propia l贸gica del estado

El funcionamiento es el mismo que con reduce, recibimos primero el acumulador, que en este caso ser谩 nuestro estado, y despu茅s el nuevo valor del estado

A帽adiendo complejidad al estado

Aqu铆 se podr铆a argumentar que el c贸digo inicial con useState es mejor, b谩sicamente porque se entiende mejor y es m谩s corto

Pero la extracci贸n posterior ya sugiere que escala mejor en escenarios m谩s complejos

Vamos a verlo a帽adiendo un estado para cada tarea

jsx
import React, { useRef, useReducer } from 'react'
export const App = () => {
const inputRef = useRef()
const [elements, setElements] = useReducer((state, action) => {
return action.type === 'add'
? [{ value: action.value, status: action.status }, ...state]
: action.type === 'remove'
? [...state.slice(0, action.i), ...state.slice(action.i + 1)]
: action.type === 'set'
? state.map((el, j) => (j === action.i ? { ...el, status: action.status } : el))
: []
}, [])
const addElement = () =>
setElements({
type: 'add',
value: inputRef.current.value,
status: 'pending',
})
const removeElement = i => setElements({ type: 'remove', i: i })
const doneElement = i => setElements({ type: 'set', i: i, status: 'done' })
return (
<div>
<input ref={inputRef} />
<button onClick={addElement}>鉁忥笍</button>
{elements.map((el, i) => (
<div key={'element' + i}>
<span>
{el.value} {el.status}
</span>
<button onClick={() => doneElement(i)}>鉁旓笍</button>
<button onClick={() => removeElement(i)}></button>
</div>
))}
</div>
)
}
export default App
Ver en CodeSandBox

Al igual que antes, este c贸digo nos permite entenderlo todo muy r谩pidamente excepto la funci贸n useReducer

Esto es bueno ya que tendremos una idea de qu茅 hace esa parte del c贸digo y podremos seguir adelante

Si lo hacemos con useState estaremos exactamente en la misma situaci贸n

jsx
import React, { useRef, useState } from 'react'
export const App = () => {
const inputRef = useRef()
const [elements, setState] = useState([])
const setElements = action =>
setState(
action.type === 'add'
? [{ value: action.value, status: action.status }, ...elements]
: action.type === 'remove'
? [...elements.slice(0, action.i), ...elements.slice(action.i + 1)]
: action.type === 'set'
? elements.map((el, j) => (j === action.i ? { ...el, status: action.status } : el))
: []
)
const addElement = () =>
setElements({
type: 'add',
value: inputRef.current.value,
status: 'pending',
})
const removeElement = i => setElements({ type: 'remove', i: i })
const doneElement = i => setElements({ type: 'set', i: i, status: 'done' })
return (
<div>
<input ref={inputRef} />
<button onClick={addElement}>鉁忥笍</button>
{elements.map((el, i) => (
<div key={'element' + i}>
<span>
{el.value} {el.status}
</span>
<button onClick={() => doneElement(i)}>鉁旓笍</button>
<button onClick={() => removeElement(i)}></button>
</div>
))}
</div>
)
}
export default App
Ver en CodeSandBox

Es decir, aqu铆 lo entendemos todo a la perfecci贸n excepto la funci贸n setElements, que es la parte que antes estaba integrada en el useReducer

Por lo tanto la decisi贸n no es si utilizar useState o useReducer, sino en c贸mo organizar la modificaci贸n de nuestro estado

  • Podemos trabajar con distintos estados

  • Podemos trabajar con un 煤nico estado y modificarlo con distintas funciones

  • Podemos trabajar con un 煤nico estado y modificarlo con una 煤nica funci贸n

Depende de nuestras necesidades escogeremos una estrategia u otra, y entonces es cuando el useReducer nos puede dar la oportunidad de ahorrarnos algunas l铆neas de c贸digo que siempre se agradece

El objetivo siempre tiene que ser augmentar la legibilidad del c贸digo, o en otras palabras, aplicar todo lo que se puede el principio KISS

馃檵鈥嶁檪锔

Qu茅 tal la entrada?

馃憣 Bien 馃檶馃檶
馃憤 Bien, pero algunas cosas podr铆an explicarse mejor 馃槵
馃し鈥嶁檪锔 Da por sentadas demasiadas cosas 馃槖
馃し鈥嶁檪锔 A ver, hay poca chicha 馃槵
馃し鈥嶁檪锔 Los ejemplos no son muy claros 馃檱鈥嶁檪锔
馃し鈥嶁檪锔 No se entiende, est谩 mal escrito 馃憥
鉁嶏笍 Hay errores, rev铆salo en cuanto puedas 馃檹
Enviar Feedback 鉁嶏笍
El texto est谩 en blanco!
Gracias por enviarme tu opini贸n
馃憤

Si quieres explorar m谩s cursos y m谩s entradas en el blog, los tienes todos en la p谩gina principal, y si el contenido te ha ayudado si lo compartes me ayudas a dar difusi贸n 馃憤

Privacidad
by kuworking.com
[ 2020 >> kuworking ]