Redux en React

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.

Permite definir estados globales.

Existen varias implementaciones de Redux que puedes utilizar. Nosotros utilizaremos la implementación recomendada oficialmente desde el año 2019: Redux Toolkit.

La principal diferencia entre Redux Toolkit y otras implementaciones es que en Redux Toolkit no es necesario declarar los objetos actions. Estos objetos son creados y gestionados automáticamente por Redux Toolkit. Sirven para determinar el reducer (la función que modificará la store) que debe ejecutarse.

No tiene sentido usar Redux y useState para una misma variable. Si una variable es global a nivel de aplicación, no tiene sentido hacerla global a nivel de componente. Estaríamos guardando la aplicación dos veces.

Diferencias entre Context API y Redux

  • Redux es una librería totalmente externa a react.
  • Redux usa middlewares de serie (es decir, permite ejecutar funciones cuando una variable ha cambiado). Esto es útil, por ejemplo, si queremos trackear con google analytics cuando el usuario cambia de url.
  • Con context API, al actualizarse una propiedad de un objeto, se actualizarán todos los componentes, mientras que con Redux, sólo se actualizarán los componentes que utilicen dicha propiedad. Por tanto, usaremos Context API con pequeños cambios en los estados globales y Redux para grandes aplicaciones con cambios constantes en los estados globales.

Creación de un proyecto básico con Redux

npm i redux react-redux
npm i @reduxjs/toolkit
  • src
    • store
      • index.js
      • user
        • index.js
        • selectors.js
    • components
      • Consumer1.js
      • Consumer2.js

App

Un provider es un componente que hace visibles ciertos datos o métodos para ser usados en sus nodos hijos.

Redux modificará la store, que es donde se encuentran las variables globales que afectan a toda la aplicación.

Para ello será necesario envolver todos los componentes que van a poder acceder a la store dentro de un provider que tendrá la store como prop.

./src/App.js
import { Provider } from "./store";
import Consumer1 from "./Consumer1";
import Consumer2 from "./Consumer2";

const App = () => {
    return (
        <Provider>
            <Consumer1 />
            <Consumer2 />
        </Provider>
    )
}

export default App;

La store

El store un objeto que contiene todo el árbol de estados de la aplicación. Un estado es una propiedad del store.

El código que define la store es siempre igual, prácticamente sólo cambian los middlewares que queremos utilizar (para mostrar trazas, para almacenar el estado global en el localStorage, etc).

En el store se registran los reducers, que como veremos más adelante serán los que modifiquen los estados.

./src/store/index.js
import { configureStore } from "@reduxjs/toolkit";
import { Provider as ReduxProvider, useSelector, useDispatch } from "react-redux";
import user from "./user";

const store = configureStore({ reducer: { user } });

const Provider = ({ children }) => <ReduxProvider store={store}>{children}</ReduxProvider>

export {
    Provider,
    useSelector,
    useDispatch
};

Los reducers

Los reducers materializarán los cambios en el estado global de la aplicación.

./src/store/user/index.js

import { createSlice } from '@reduxjs/toolkit'

const initialState = {
    name: null
};

const useSlice = createSlice({
    name: 'user',
    initialState,
    reducers: {
        setName: (state, action) => {
            state.name = action.payload;
        },
    }
});

export const { setName } = useSlice.actions;

export default useSlice.reducer;

./src/store/user/selectors.js

export const selectUserName = (state) => state.user.name;

En el reducer.js exportamos el reducer y los selectores.

Los componentes

Dentro del método dispatch pondremos el nombre del reducer que queremos ejecutar.

./src/components/Consumer1.js
import { useDispatch } from "../store";
import { setUserName } from "../store/user";

const Consumer1 = () => {
    const dispatch = useDispatch();
    const onNameChange = e => dispatch(setName(e.target.value));

    return (
        <div>
            <input type="text" onChange={onNameChange} />
        </div>
    )
}

export default Consumer1;

./src/components/Consumer2.js

import { useSelector } from "../store";
import { selectUserName } from "../store/user/selectors";

const Consumer2 = () => {
    const name = useSelector(selectName);

    return (
        <div>{name}</div>
    )
}

export default Consumer2;

Ejercicio: Añadiendo una propiedad a la entidad user

Añadiendo una nueva entidad

Ejercicios Redux

1. Añadir la propiedad age a la entidad user.

2. Añadir la entidad AppPreferences con la propiedad theme.

3. Modificaremos los ejercicios que vimos en la sección de recogida de datos:

  • Calcular grados Fahrenheit.
  • Convertir euros en dólares.
  • Calcular la superficie de un rectángulo.

Cada uno de estos ejercicios debe tener dos componentes. Uno en el que se introducirán los datos y otro en el que se pintarán los resultados. Utilizaremos redux para compartir del valor a través de los diferentes componentes.

const App = () => (
    <Provider>
        <Form/>
        <PintarResultado/>
    </Provider>
);

export default App;

Ejercicio listado con Redux

Crear un componente que consulte un array con un listado de valores y los muestre por pantalla.

Al pulsar sobre un elemento del listado, debemos almacenar su id con Redux y utilizarla para pintar el título de ese elemento recuperándolo con Redux desde otro componente.

El componente del listado, quedaría de la siguiente forma:

Ejercicio listado de personas con Redux

El componente (Persona) debe recibir la prop del evento que se ha producido.

Listado
{ elementos.map(({ id, name }) => (
  <Persona
    onClick={() => setActiveElement(id)}
    key={id}
    nombre={name} />
  ))}
Persona
const Persona = ({ nombre, onClick }) => <div onClick={onClick}>{nombre}</div>
← Context API
Aplicación tienda online - por pasos →