Redux: Estados Globales en React

thorcazo - Aug 18 - - Dev Community

Introducción

En estas últimas semanas estoy aprendiendo React, creando pequeñas aplicaciones y haciendo pruebas. En el FP nos enseñaron cómo funciona Angular; creación de componentes, servicios, comunicación entre componentes con @Inputs y @Outputs, etc. Angular es un framework muy completo que ofrece todo lo necesario para crear cualquier tipo de aplicación desde el principio. En cambio, aunque React también se utiliza para construir aplicaciones frontend, su entorno y forma de trabajo son diferentes. Los componentes en React no se crean de la misma manera que en Angular; React es más flexible y, en muchos casos, requiere la instalación de librerías adicionales, tanto oficiales como de terceros, lo cual no es negativo, pero implica que se debe conocer bien el entorno de React para aprovechar al máximo su potencial. En este artículo explicaré mi experiencia utilizando Redux.

Estados globales en React

Dejando Angular para otro momento, quiero hablaros de Redux, una librería muy potente que me ha ayudado a manejar estados en React. Antes de conocer Redux, me encontré con un problema común en React: cómo gestionar el estado de forma eficiente entre dos componentes completamente independientes. Aunque React ofrece soluciones para manejar el estado local, compartir este estado entre componentes que no están directamente relacionados puede volverse complicado y propenso a errores. Fue en ese momento cuando mi amigo ChatGPT me sugirió que una de las herramientas más efectivas para resolver este problema es Redux.

Vamos a lo práctico: Instalación de Redux

La aplicacion está creada con vite, he obviado la instalación inicial de la aplicación para no alargar demasiado.
Apartir de este punto se instala las depencias necesarias y sólo las más importantes. Obviando también tailwind CSS

Aquí tengo un ejemplo con un CSS básico.

ejemplo_counter

En el ejemplo se muestra el incremento del número con los siguientes botones que modifican el estado.

npm install @reduxjs/toolkit
Enter fullscreen mode Exit fullscreen mode

Se necesitará también los bindings react-redux para usarlos con React

npm install react-redux
Enter fullscreen mode Exit fullscreen mode

Creamos el Slice del Contador

Aquí definimos un slice para manejar el estado del contador. Creamos un estado inicial initialState: { value: 0 } y 4 reducers:

  • increment
  • decrement
  • incrementByAmount
  • reset

Creo que no hace falta explica qué hace cada reducer.

import { createSlice } from "@reduxjs/toolkit";

export const counterSlice = createSlice({
  name: 'counter',
  initialState: { value: 0 },
  reducers: {
    increment: (state) => { state.value += 1 },
    decrement: (state) => { state.value -= 1 },
    incrementByAmount: (state, action) => { state.value += action.payload },
    reset: (state) => { state.value = 0 }
  }
})

export const { increment, decrement, reset, incrementByAmount } = counterSlice.actions;
export const selectCount = (state) => state.counter.value;

export default counterSlice.reducer;

Enter fullscreen mode Exit fullscreen mode

Para los estados globales, necesitamos un store . Creamos el archivo con el mismo nombre store.js para configurar usando el reducer de counterSlice.js . Este store almacena todos los estados globales del esta mini aplicación.

import { configureStore } from '@reduxjs/toolkit'
import counterReducer from '../features/counter/counterSlice'

export default configureStore({
  reducer: { 
    counter: counterReducer,
   }
})

Enter fullscreen mode Exit fullscreen mode

Ahora, cualquier aplicación que esté conectado a este store podrá acceder al estado del contador y “despachar acciones” que ayudan a modificarlo.

Envolviendo main.jsx

Para que Redux funcione correctamente en nuestra aplicación, necesitamos envolver la raíz de nuestra aplicación con el componente <Provider> que proviene de react-redux. Esto permite que toda la aplicación tenga acceso al store de Redux. Aquí te muestro cómo hacerlo en main.jsx:

import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import App from './App.jsx'
import './index.css'

import store from './app/store.js'
import { Provider } from 'react-redux'

createRoot(document.getElementById('root')).render(
  <Provider store={store}>
    <StrictMode>
      <App />
    </StrictMode>
  </Provider >
)
Enter fullscreen mode Exit fullscreen mode

Creamos componente Counter.jsx

Este componente será en encargado de mostrar la información y modificar los datos del contador. Aquí utilizaremos todos los reducers que hemos configurado.

import React, { useState } from 'react'
import { useSelector, useDispatch } from 'react-redux'
import { increment, decrement, reset, incrementByAmount } from '../features/counter/counterSlice'

export default function Counter() {

  const count = useSelector((state) => state.counter.value)
  const dispatch = useDispatch();

  const [incrementAmount, setIncrementAmount] = useState('2');

  return (
    <>
      <section className='flex flex-col justify-center items-center gap-8'>
        <div>
          <div className='text-center p-8'>
            <h1 className='text-5xl uppercase'>Counter</h1>
          </div>
          <section className='flex gap-4 items-center flex-col w-full'>
            <p className='text-5xl'>{count}</p>
            <div className='flex items-center gap-4'>
              <button className='btn-green' onClick={() => dispatch(increment())}>Increment</button>
              <button className='btn-red' onClick={() => dispatch(decrement())}>Decrement</button>
              <button className='btn-reset' onClick={() => dispatch(reset())}>Reset</button>
            </div>
          </section>
        </div>

        <div className='flex justify-center'>
          <div className='py-8 px-8 bg-neutral-800 border border-neutral-500 rounded'>
            <p className='py-4 rounded mb-2 mt-4'>Enter a number to increment the counter by that amount:</p>
            <div className='flex flex-row'>
              <input className='w-full p-4 rounded text-xl' type="number"
                value={incrementAmount}
                onChange={(e) => setIncrementAmount(e.target.value)}
              />
              <button className='py-2 px-4 border border-neutral-500 rounded hover:bg-neutral-500 hover:text-white transition duration-200 ease-in-out'
                onClick={() => dispatch(incrementByAmount(Number(incrementAmount) || 0))}
              >
                Add
              </button>
            </div>
          </div>
        </div>
      </section >
    </>
  )
}

Enter fullscreen mode Exit fullscreen mode

No olvides importar <Counter/> en App.jsx

import './App.css'
import Counter from './components/Counter'
function App() {
  return (
    <>
      <Counter />
    </>
  )
}

export default App
Enter fullscreen mode Exit fullscreen mode

Estilos CSS para con componente.

He utilizado estos estilos con Tailwindcss para el componente, los puedes utilizar u omitir, pero te servirán de referencia.

@tailwind base;
@tailwind components;
@tailwind utilities;

:root {
  color-scheme: light dark;
}

.btn-green {
  @apply px-6 py-2 bg-neutral-800 border border-neutral-500 rounded hover:bg-green-900 text-white transition-all;
}

.btn-red {
  @apply px-6 py-2 bg-neutral-800 border border-neutral-500 rounded hover:bg-red-900 text-white transition-all;
}

.btn-reset {
  @apply px-6 py-2 bg-neutral-800 border border-neutral-500 rounded hover:bg-yellow-700 text-white transition-all;
}

Enter fullscreen mode Exit fullscreen mode

Para terminar

Redux es una herramienta poderosa, que combinada con React te permite crear aplicaciones mucho más cómodo al manejar los estados globales. Con cierta práctica te darás cuenta que siempre te será útil en cada desarrollo de aplicación. Hay que quedarse con los conceptos básicos como:

  • reducers
  • store
  • slices

De esta manera no sólo te darás cuenta puedes manejar los estados de forma fácil si que que mejorará la organización de la aplicación y su estructura. Por último, recuerda que no siempre es necesario utilizar herramientas como Redux, hay que evaluar la magnitud del proyecto y decidir si es buena idea incluirlo.

.
Terabox Video Player