margin-js | kuworking

Gatsby11tyAstro|UnoCSS|SolidJS

Utilizando useReducer

Ojo, este artículo no se ha actualizado desde hace más de 12 meses

Si reduce es la versión completa de forEach y demás, useReducer vendría a ser la versión completa de useState

Por ejemplo, tenemos esta aplicación para añadir y quitar elementos de una variable controlada por useState

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>
  )
}

Ahora lo refactorizo para extraer la lógica y hacerlo más legible (o no)

import React, { useRef, useState } from 'react'

export const App = () => {
  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 = 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>
  )
}

Creo la función changeElements para poder simplificar las funciones addElement y removeElement, lo cual no me queda claro que mejore o empeore la legibilidad del tema pero ahí está

El ejemplo me viene bien porque puedo utilizar useReducer para juntar el useState con el changeElements

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>
  )
}

Conceptualmente estoy juntando todo lo ofuscado dentro del useReducer mientras el resto del código es fácil de entender

Esto en general es bueno, y mejora en cuanto acostumbras la vista al reduce y al useReduce