Separación de Reducers en Redux con CombineReducers

Clase 19 de 22Curso Profesional de React.js y Redux

Resumen

¿Por qué es importante separar responsabilidades en nuestro estado?

Separar las responsabilidades de nuestro estado es clave para mantener aplicaciones escalables y ordenadas. En una aplicación sencilla, puede parecer innecesario dividir el estado en diferentes partes, pero a medida que aumenta la complejidad y el estado obtiene más niveles de profundidad, aplicar el principio de "divide y vencerás" se vuelve inevitable. Un ejemplo claro se puede ver en una plataforma como DenPlatzi, donde se gestionan diferentes tipos de información, como el perfil del usuario, el temario del curso y aportes de la comunidad. Un estado monolítico haría que el código fuera más difícil de manejar, entender y mantener. Separar el estado ayuda a cumplir con el principio de separación de responsabilidades, lo cual facilita la mantenibilidad y claridad del código.

¿Cómo utilizar Combine Reducers en Redux?

Redux ofrece un helper denominado combineReducers. Este tiene como objetivo separar trozos del estado según sus responsabilidades, mejorando así la experiencia de desarrollo sin ser una obligación.

Veamos cómo utilizar esta herramienta paso a paso:

  1. Crear reducers especializados: Separamos las funcionalidades del estado original, creando reducers individuales para cada sección del estado.

    • Reducer para la data de Pokémones:

      // Inicializar el estado y gestionar acciones relacionadas con Pokémon
      const pokemonsReducer = (state = initialState, action) => {
        switch (action.type) {
          case 'SET_POKEMONS':
            return { ...state, pokemons: action.payload };
          // Otros casos aquí...
          default:
            return state;
        }
      };
      
    • Reducer para la interfaz de usuario (UI):

      // Gestión de la propiedad 'loading' en la UI
      const uiReducer = (state = { loading: false }, action) => {
        switch (action.type) {
          case 'SET_LOADING':
            return { ...state, loading: action.payload };
          default:
            return state;
        }
      };
      
  2. Unir Reducers con combineReducers:

    • Crear un archivo denominado rootReducer.js e importar combineReducers.
    • Unir los reducers especializados:
      import { combineReducers } from 'redux-immutable'; // Asegurarse de usar la versión correcta al trabajar con immutable.js
      
      const rootReducer = combineReducers({
        data: pokemonsReducer,
        ui: uiReducer,
      });
      
      export default rootReducer;
      
  3. Actualizar createStore:

    • Asegúrate de que createStore utilice el rootReducer en lugar de un único reducer:
      import { createStore } from 'redux';
      import rootReducer from './rootReducer';
      
      const store = createStore(rootReducer);
      

¿Cómo afecta useSelector al rendimiento?

Una técnica crucial para optimizar el rendimiento de aplicaciones en React es comprender cómo funciona useSelector. Este hook de React Redux es utilizado para extraer datos del estado, pero hay que tener cuidado con los re-renders innecesarios.

  • Comparación estricta vs. comparativa superficial:

    • Por defecto, useSelector utiliza una comparación estricta (===) para determinar si debe volver a renderizar un componente. Esto puede resultar en re-renders innecesarios cuando se trabaja con estructuras de datos complejas, ya que la inmutabilidad crea nuevas referencias incluso con los mismos valores.

    • Para evitar esto, se utiliza shallowEqual de React Redux, el cual realiza una comparación superficial, evaluando los valores de las referencias de objetos y arreglos en lugar de las referencias en sí:

      import { useSelector, shallowEqual } from 'react-redux';
      
      const pokemons = useSelector(state => state.data.pokemons, shallowEqual);
      
  • Loading boolean: Para valores booleanos como loading, no es necesaria la comparación superficial, ya que la comparación estricta es suficiente.

Con la estrategia adecuada de organización de estado usando combineReducers, así como optimizando useSelector con shallowEqual, se logra obtener una aplicación más eficiente, fácil de entender y mantener. Sigue explorando y adaptando estas técnicas para asegurar que tu aplicación sea robusta y flexible. ¡La siguiente lección nos llevará un paso más allá con el uso de Redux Toolkit! Avanza con confianza y motivación en este emocionante viaje de aprendizaje en Redux y React.