DEV Community

Julian Kaymer Agama Tanta
Julian Kaymer Agama Tanta

Posted on

🧬Hooks de React: useReducer

📦 ¿Qué es useReducer?

Es un hook que te permite manejar estados complejos o con múltiples transiciones de forma predecible y organizada.

✅ Úsalo cuando:

  • Tienes múltiples subvalores en el estado
  • El siguiente estado depende del anterior
  • Quieres mover la lógica de actualización a una función separada

🧪 Sintaxis básica

const [state, dispatch] = useReducer(reducer, initialState);
Enter fullscreen mode Exit fullscreen mode
  • reducer: una función (state, action) => newState
  • initialState: estado inicial
  • dispatch: función que usas para lanzar acciones

🧠 ¿Qué es un reducer en JavaScript?

Antes de seguir aventurándonos en el useReducer, conozcamos las bases.
Un reducer es una función que toma una colección de elementos (como un array) y los reduce a un único valor, he ahí el nombre de reducer (reductor en español).

🧪 Sintaxis básicas

Se tiene 2 valores, un callback y un valor inicial .

const resultado = array.reduce(callback, valorInicial)
Enter fullscreen mode Exit fullscreen mode

Sintaxis del callback

(acumulador, valorActual) => {}
Enter fullscreen mode Exit fullscreen mode
  • acumulador: Guardará en un inicio el valor inicial que se le paso al reduce, y luego según se opere.
  • valorActual: El elemento actual del array que estamos procesando

Ejemplo

const numeros = [1, 2, 3, 4];

const suma = numeros.reduce((acumulador, valorActual) => {
  return acumulador + valorActual;
}, 0); // El 0 es el valor inicial del acumulador

console.log(suma); // 10
Enter fullscreen mode Exit fullscreen mode

Y el .reduce() hace algo así internamente:

acumulador = 0 (valor inicial)
1 → acumulador = 0 + 1 = 1
2 → acumulador = 1 + 2 = 3
3 → acumulador = 3 + 3 = 6
4 → acumulador = 6 + 4 = 10

Enter fullscreen mode Exit fullscreen mode

Entendiendo esto, podemos pasar a:

¿Cómo puedo aplicar el useReducer en mi proyecto?

Imaginemos que tenemos un formulario con 3 pasos:

  1. Información personal (name, email)
  2. Dirección (country, city)
  3. Revisión y envío

Vamos a controlar:

  • Los datos del formulario,
  • El paso actual,
  • Acciones para avanzar, retroceder o actualizar datos.

🔧 Lógica del reducer y tipado

// 🧱 Tipos del estado
type FormData = {
  name: string;
  email: string;
  country: string;
  city: string;
};

type State = {
  currentStep: number;
  data: FormData;
};

// 🎯 Tipos de acciones
type Action =
  | { type: 'NEXT_STEP' }
  | { type: 'PREV_STEP' }
  | { type: 'UPDATE_FIELD'; field: keyof FormData; value: string }
  | { type: 'RESET_FORM' };

// 🎛️ Estado inicial
const initialState: State = {
  currentStep: 0,
  data: {
    name: '',
    email: '',
    country: '',
    city: '',
  },
};

// 🔄 Reducer
function formReducer(state: State, action: Action): State {
  switch (action.type) {
    case 'NEXT_STEP':
      return { ...state, currentStep: state.currentStep + 1 };
    case 'PREV_STEP':
      return { ...state, currentStep: state.currentStep - 1 };
    case 'UPDATE_FIELD':
      return {
        ...state,
        data: { ...state.data, [action.field]: action.value },
      };
    case 'RESET_FORM':
      return initialState;
    default:
      return state;
  }
}

Enter fullscreen mode Exit fullscreen mode

🧠 ¿Cómo se usa en un componente?

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

// Para avanzar
dispatch({ type: 'NEXT_STEP' });

// Para retroceder
dispatch({ type: 'PREV_STEP' });

// Para actualizar un campo
dispatch({ type: 'UPDATE_FIELD', field: 'email', value: '[email protected]' });

// Para reiniciar el formulario
dispatch({ type: 'RESET_FORM' });

Enter fullscreen mode Exit fullscreen mode

🧰 Bonus: 3er parámetro de useReducer

Este tercer parámetro es un parámetro opcional

const [state, dispatch] = useReducer(reducer, initialArg, init?);
Enter fullscreen mode Exit fullscreen mode
  1. reducer: la función reductora
  2. initialArg: valor inicial del estado, que puede ser un estado directo o un argumento para construirlo
  3. init(opcional): una función que inicializa el estado a partir de initialArg

🤔 ¿Para qué sirve el tercer parámetro?

Sirve para calcular el estado inicial de forma perezosa (lazy), es decir, solo se ejecuta una vez, al montar el componente.

Esto es útil cuando:

  • El estado inicial requiere cálculos pesados
  • Quieres construir el estado inicial desde una prop o valor externo
  • Quieres usar algo como JSON.parse(localStorage.getItem(...))

🧪 Ejemplo: Cargar estado desde localStorage

type State = { theme: 'light' | 'dark' };

function reducer(state: State, action: { type: 'TOGGLE_THEME' }): State {
  switch (action.type) {
    case 'TOGGLE_THEME':
      return { theme: state.theme === 'light' ? 'dark' : 'light' };
    default:
      return state;
  }
}

// Función init: se ejecuta una vez para calcular el estado inicial
function init(initialArg: string | null): State {
  const parsed = initialArg ? JSON.parse(initialArg) : null;
  return parsed ?? { theme: 'light' };
}

const [state, dispatch] = useReducer(reducer, localStorage.getItem('theme'), init);
Enter fullscreen mode Exit fullscreen mode

🧠 Casos de uso comunes

  • Formularios con validación compleja
  • Manejo de múltiples estados relacionados
  • UIs con múltiples interacciones o pasos (wizards, tabs, etc.)

Fuentes y algunos artículos que quizá te interesen

Top comments (0)