🖖 Inicio

SERVICIOS

Salir

nochedía

Utilizando useReducer

400 palabras
2 minutos
March 16, 2020
blogjavascript

useReducer nos permite integrar una función en useState y poder trabajar más cómodamente con estados complejos

useReducer

Para qué nos sirve useReducer? En qué se parece a useState? Cuándo utilizar uno u otro?

Buena pregunta

Los hooks de React ya sabes que son maneras de controlar el estado extremadamente directas y claras y sin necesidad de utilizar clases

El ejemplo de useState es el más sencillo

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

Éste sería el ejemplo básico

El hook useState nos devuelve dos variables, la primera es el estado en sí, la segunda es la función que utilizamos para cambiar ese estado

Por convenio, los nombres que se sugieren son variable y setVariable (y no veo por qué no seguir el convenio)

Y el valor inicial es el que le damos a la función useState()

Este valor inicial puede ser cualquier cosa, también un objeto

js
const [vari, setVari] = useState({ status: 'pending', value: null })
setVari({ status: 'ok', value: 'whatever' })

Aquí almacenamos un objeto con las propiedades status y value, y luego las cambiamos asignando otro objeto con las mismas propiedades y distintos valores

Y cada vez que cambiamos el valor del estado, react arranca un re-renderizado o repaint

Si no queremos un repaint (pero queremos guardar el estado), entonces utilizaríamos useRef

Ojo porque un error típico sería cambiar las propiedades del objeto pero no el objeto en sí, y entonces no habría ningún repaint

Por ejemplo

js
// no funciona como esperaríamos
const [vari, setVari] = useState({ status: 'pending', value: null })
vari.status = 'ok'
setVar(vari) // para react el objeto no ha cambiado puesto que está almacenando la misma referencia

Bien

Entonces, cuándo utilizamos useReducer? Qué nos ofrece diferente a useState?

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

Lo primero, la utilidad del hook: useReducer nos facilita el tratar con estados complejos con los que con useState nos quedaríamos cortos

Ejemplos?

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

js
const Todo = () => {
const inputRef = useRef()
const [elements, setElements] = useState([])
const addElement = () => setElements([inputRef.current.value, ...elements])
const removeElement = i => setElements([...elements.slice(0, i), ...elements.slice(i + 1)])
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>
)
}

Con esto ya tenemos una rutina que nos añade o quita elementos de un estado que definimos como un array

Lo reescribo para encapsular más la lógica, aunque el resultado final es peor IMO pero nos sirve para compararlo luego con el useReducer

js
const Todo = () => {
const inputRef = useRef()
const [elements, setElements] = useState([])
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 = () => setElements(i => 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>
)
}

La ventaja de esta manera de escribir el código es que es todo más abstracto ya que toda la lógica está en changeElements, el problema es que esta función accede a elements y queda un poco caótico

Esto lo solucionamos con useReducer, que al igual que hace un reduce al uso, nos envía el valor previo de la variable en la función con la que operamos

Mejor verlo en código

js
const Todo = () => {
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>
)
}

Como ves, lo que nos permite useReducer es integrar una función dentro de lo que sería un useState con la que podemos hacer lo que queramos, y por lo tanto se entiende que nos servirá para estados más complejos

Su uso es como un reduce

Su definición es como haber hecho una mezcla entre reduce() y useState()

En este ejemplo? Yo me quedo con el useState por permitir un código más conciso, pero ya se ve el potencial que tiene useReduce

Listos! 🙋‍♂️

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 🙏

Newsletter de kuworking, un correo de tanto en cuanto

Quizá te interese

Privacidad
by kuworking.com
[ 2020 >> kuworking ]