Redux toolkit usado con react-query es muy poderoso. Creo que esta herramienta se merece todo un curso a parte.
Conceptos claves para empezar
Curso Profesional de React.js y Redux: Conceptos Clave y Prácticas
Conceptos Claves y Principios de Redux
Ciclo de Vida de Datos con Redux en Aplicaciones React
Comparativa entre Redux y Context API en React
Introducción a nuestro proyecto
Creación de una Pokedots con Redux y Redux Toolkit
Creación de Proyectos React con Create React App
¡Atraparlos ya!
Integración de PokeApp en Proyecto con Axios y React
React.js + Redux
Integración de Redux en Aplicaciones React
Uso de Hooks de Redux: useSelector y useDispatch
Instalación y uso de Redux DevTools en navegadores
Middlewares
Creación de Middleware Personalizado en Redux
Uso de PokeAPI para Obtener Imágenes de Pokémon en React
Integración de Redux Tunk para Lógica Asíncrona en Action Creators
Diferencias entre Redux Tonk y Redux Saga
Avanzando la ui
Implementación de Loader y Favoritos en App de Pokémones
Funcionalidad de Favoritos en Aplicación de Pokémon con Redux
Inmutabilidad
Inmutabilidad en JavaScript: ObjectAssign y SpreadOperator
Implementación de Inmutabilidad con ImmutableJS en Proyectos JavaScript
Avanzado
Separación de Reducers en Redux con CombineReducers
Redux Toolkit: Simplificación de Reducers y Slices
Asincronismo en Redux Toolkit: CreateAsyncThunk y UI Slice
Despedida del curso
Filtrado de Datos en Aplicaciones con ReWiz y Redux
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
La gestión del asincronismo con Redux Toolkit se realiza mediante el método createAsyncThunk
. Este reemplaza la necesidad del tradicional Redux Thunk, simplificando el manejo de acciones asíncronas. Aquí aprenderás cómo implementarlo eficazmente y a crear el UI Slice que falta en tu aplicación.
createAsyncThunk
es un método de Redux Toolkit que facilita la creación de acciones asíncronas. Al utilizarlo, no se requiere una configuración tan extensa como con Redux Thunk
, haciendo que el desarrollo sea más eficiente y limpio.
Pasos para usar createAsyncThunk:
createAsyncThunk
desde Redux Toolkit
.createAsyncThunk
:
export const fetchPokemonWithDetails = createAsyncThunk(
'data/fetchPokemonWithDetails',
async (_, { dispatch }) => {
// Aquí ejecutarás tus acciones asíncronas
}
);
Al crear una acción asíncrona con createAsyncThunk
, sigues una serie de pasos lógicos:
Definir el PayloadCreator: Esta es la función que se ejecutará cuando se dispare la acción. Debe ser asíncrona si va a realizar operaciones de red, como llamar a una API.
Gestionar parámetros: Si tu acción necesita recibir parámetros, se los puedes pasar como desees. Si no los necesitas, puedes ignorarlos.
Desestructuración del thunksAPI: Puedes desestructurar el dispatch
de thunksAPI
para gestionar el estado en tu Redux Store
.
Utilizar dispatch: Desde aquí puedes activar otros reducers, como hacer un dispatch de un loader, entre otros.
Redefiniendo el estado de UI en tu aplicación, puedes gestionar mejor elementos como los loaders. Aquí se muestra cómo implementar un UI Slice:
Crear el archivo uiSlice.js
:
import { createSlice } from '@reduxjs/toolkit';
const uiSlice = createSlice({
name: 'ui',
initialState: { loading: false },
reducers: {
setLoading(state, action) {
state.loading = action.payload;
}
}
});
export const { setLoading } = uiSlice.actions;
export default uiSlice.reducer;
Agregar al reducer combinado: Integra el nuevo UI Reducer
en el archivo principal de reducers de Redux.
Gestionar el loader: Mediante setLoading
, puedes encender y apagar el loader, brindando una experiencia más fluida al usuario.
Una vez que todo esté configurado, es crucial probar cómo se comporta tu aplicación en el navegador:
Verificar visualización de elementos: Comprueba que los pokémones se cargan correctamente y que las acciones (como marcar favoritos) funcionan bien.
Utilizar herramientas de desarrollo: Las herramientas como Redux DevTools te mostrarán las acciones lanzadas y el estado actual de tus slices.
Integración de middlewares: Aprovecha middlewares custom para ver cómo los actions se disparan y gestionan en tiempo real.
Estos pasos aseguran que tu integración con Redux Toolkit sea robusta y eficiente, brindando a los usuarios una experiencia fluida mientras disminuyes el código redundante en comparación con métodos tradicionales. Siguiendo esta metodología, podrás gestionar proyectos aún más complejos en el futuro usando la documentación oficial de Redux Toolkit. ¡Sigue explorando y aprendiendo!
Aportes 17
Preguntas 2
Redux toolkit usado con react-query es muy poderoso. Creo que esta herramienta se merece todo un curso a parte.
No sé, tal vez soy yo, pero… es que la profe se come muchas partes importantes de la explicacion, tal vez las supone obvias, pero en realidad creo que este curso se merece un Remake, la seguí con mucho esfuerzo hasta aquí, pero no me anda la App ni a tiros desde que empezó con toolkit
Creo que la maestra olvido implementar “configureStore” de Redux toolkit, ya que para crear el store seguimos usando el metodo legacy “createStore” importado de redux.
Leyendo la documentacion me quedo de esta manera:
import { configureStore } from '@reduxjs/toolkit';
import dataReducer from './slices/dataSlice';
import uiReducer from './slices/uiSlice';
export const store = configureStore({
reducer: {
data: dataReducer,
ui: uiReducer,
},
});
// Infer the `RootState` and `AppDispatch` types from the store itself
export type RootState = ReturnType<typeof store.getState>;
// Inferred type: {posts: PostsState, comments: CommentsState, users: UsersState}
export type AppDispatch = typeof store.dispatch;
Y asi, el codigo en el archivo index queda mucho mas limpio (ya no necesitamos hacer la validacion del compose para usare el compose correcto):
import React from 'react';
import ReactDOM from 'react-dom/client';
import { Provider } from 'react-redux';
import App from './App';
import { store } from './store';
import './index.css';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
);
Tambien al hacer esto, ya no necesitamos el root reducer, asi que elmine la carpeta “reducers” y elimine las depencias de thunk y redux-thunk.
Que desastre como se explican estos temas tan importantes!
¡Creo que mejoró mucho el código con redux-tookit!
muy bueno y fácil con redux-toolkit
Una vez más vuelve a hacer cambios que no muestra. Como quitar el hardcoding de loading que hace a false. Si no te acuerda de esto, te quedas loco pensando porqué a ti no te aparece el spin
Asi me quedo el middleware para integrarlo al sistema que usa reduxtoolkit y su metodo configureStore en el index.js general:
middlewares.js
export const capitalizePokemons = (store)=>(next)=>(action)=>{
const capitalize = (string) => {
return string.charAt(0).toUpperCase() + string.slice(1);
}
if (action.type === "data/setPokemons") {
const capitalizedPokemons = action.payload.map((pokemon) => {
return {
...pokemon,
name: capitalize(pokemon.name),
};
});
const updatedAction = {
...action,
payload: capitalizedPokemons
}
return next(updatedAction)
}
return next(action)
}
index,js
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger, capitalizePokemons)
});
Algo que no se mencionó es modificar la configuración del store con @reduxjs/toolkit.
.
Esto simplifica mucho nuestro index.jsx, ya que incluye por defecto:
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App/index'
import './index.css'
import { rootReducer } from './reducers/rootReducer'
import { Provider } from 'react-redux'
import { configureStore } from '@reduxjs/toolkit'
import { logger,nameUpperCase } from './middlewares/index'
const store = configureStore({
reducer: rootReducer,
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger, nameUpperCase)
});
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>,
)
Hola a todos, les dejo mi codigo toolkit - ts - vite con algunas modificaciones respecto a la clase:
// main.tsx
import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import { Provider } from "react-redux";
import { store } from './slices';
ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(
<React.StrictMode>
<Provider store={store} >
<App />
</Provider>
</React.StrictMode>
)
// slices/index.ts
import { configureStore } from "@reduxjs/toolkit";
import { TypedUseSelectorHook, useDispatch, useSelector } from "react-redux";
import { dataReducer } from "./dataSlice";
import { uiReducer } from "./uiSlice";
const store = configureStore({
reducer: {
data: dataReducer,
ui : uiReducer
}
})
type RootState = ReturnType<typeof store.getState>
type Appdispatch = typeof store.dispatch
export const useAppDispatch : ()=> Appdispatch = useDispatch;
export const useAppSelector : TypedUseSelectorHook<RootState> = useSelector;
export { store }
// slices/dataSlice.ts
import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import { getPokemonDetails, getPokemons, PokemonDetailType, PokemonType } from "../api";
import { setLoading } from "./uiSlice";
type InitialState = {
pokemons: PokemonDetailType[]
}
const initialState: InitialState = {
pokemons: []
}
export const fetchPokemonsWithDetails = createAsyncThunk(
'data/fetchPokemonsWithDetails',
async (_,{dispatch}) =>{
try {
dispatch(setLoading(true))
const pkmns = await getPokemons();
const pkmnsDetailed = await Promise.all((pkmns as PokemonType[]).map(pkmn=>getPokemonDetails(pkmn)))
dispatch(setPokemons(pkmnsDetailed))
dispatch(setLoading(false))
} catch (err) {
console.error(err)
dispatch(setLoading(false))
}
}
)
export const dataSlice = createSlice({
name: 'data',
initialState,
reducers:{
setPokemons: (state,action)=>{
state.pokemons = action.payload
},
setFavorite: (state, action)=>{
const currentPokemonIndex = state.pokemons.findIndex(
(pkmn:PokemonDetailType) => {
return pkmn.id === action.payload.pokemonId
}
)
if(currentPokemonIndex >= 0){
const isFavorite = state.pokemons[currentPokemonIndex].favorite
state.pokemons[currentPokemonIndex].favorite = !isFavorite
}
}
}
})
export const dataReducer = dataSlice.reducer;
export const { setPokemons, setFavorite } = dataSlice.actions;
// slices/uiSlice.ts
import { createSlice } from "@reduxjs/toolkit"
export type TypeState = {
loading : boolean
}
const initialState:TypeState = {
loading: false
}
const uiSlice = createSlice({
name: 'ui',
initialState,
reducers: {
setLoading: (state, action)=>{
state.loading = action.payload
}
}
})
export const uiReducer = uiSlice.reducer;
export const { setLoading } = uiSlice.actions;
// App.tsx
import { useEffect } from 'react'
import { Search } from './components/Search'
import { PokemonList } from './components/PokemonList'
import { Col, Spin } from 'antd'
import { useAppDispatch, useAppSelector } from './slices'
import { fetchPokemonsWithDetails } from './slices/dataSlice'
import logo from './statics/logo.svg'
import './App.css'
function App() {
const pokemons = useAppSelector((state)=>state.data.pokemons)
const loading = useAppSelector((state)=>state.ui.loading)
const dispatch = useAppDispatch();
useEffect(()=>{
dispatch(fetchPokemonsWithDetails())
},[])
return (
<div className="App">
<Col span={4} offset={10}>
<img src={logo} alt="Pokedux" />
</Col>
<Col span={8} offset={8}>
<Search />
</Col>
{loading?
<Col offset={12} >
<Spin spinning size='large' />
</Col>
:
<PokemonList pokemons={pokemons} />
}
</div>
)
}
export default App;
Moraleja, redux es, al menos hasta tener proyectos realmente grandes, usar las fases básicas junto a middleware y redux toolkit.
Esta cool RTK. Tambien existe una nueva funcionalidad de RTK que es RTK Query que permite el manejo de queries y mutations de una forma mejorada. Compite directamente con React Query.
Por debajo funciona con este createAsyncThunk pero añade más funcionalidades.
https://redux.js.org/tutorials/essentials/part-7-rtk-query-basics#rtk-query-overview
En angular programamos con mucho menos Código fuente
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?