Para los despistados como yo.
En el Index.js agreguen COMPOSE__, no es el mismo que teniamos antes.
Tiene que quedar así:
const composeAlt = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
Conceptos claves para empezar
¿Ya tomaste el Curso Básico de Redux?
Conceptos claves de Redux
Ciclo de vida de Redux
Diferencias entre Redux y Context
Introducción a nuestro proyecto
Creemos una Pokedux
Iniciando nuestro proyecto
¡Atraparlos ya!
Introducción a PokeAPI
React.js + Redux
Integrando Redux
Hooks vs. Connect
Redux DevTools
Middlewares
Middlewares
Peticiones asíncronas
Redux Thunk
Middlewares alternativos: Redux Saga
Avanzando la ui
Agreguemos un loader
Agreguemos favoritos
Inmutabilidad
¿Qué es inmutabilidad?
Agregando Inmutabilidad a nuestra Pokedux
Avanzado
Cuándo usar reducers combinados
Redux Toolkit: creando nuestro primer Slice
Redux Toolkit: createAsyncThunk
Despedida del curso
Conclusiones
No tienes acceso a esta clase
¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera
Mariangélica Useche
Aportes 12
Preguntas 4
Para los despistados como yo.
En el Index.js agreguen COMPOSE__, no es el mismo que teniamos antes.
Tiene que quedar así:
const composeAlt = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
Redux Thunk
- Es un Middleware que extiende las capacidades del store (enhancer), nos permite realizar acciones asíncronas en nuestros actions creators que por defecto no podemos hacer
- Thunk es un concepto de programación donde se utiliza una función intermedia para retrasar la evaluación o ejecución de una operación, como puede ser la petición de una data a una API y demás acciones
para este caso en particular separaremos responsabilidades, delegando la petición a la API a nuestro action creator y no a el componente como se venia trabajando
Instalación
npm install redux-thunk
Integración
simplemente debemos importarlo y añadirlo a nuestra composición de middlewares
src/index.js:
import thunk from 'redux-thunk';
.....
....
...
.
const composeEnhancers = compose(
applyMiddleware(thunk, logger) // **
)
const store = createStore(
pokemonsReducer,
composeEnhancers
)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
)
si estamos haciendo uso de la herramienta Redux DevTools debemos hacer pasos adicionales
src/index.js:
import thunk from 'redux-thunk';
.....
...
.
const composeAlt = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE_ || compose
const composeEnhancers = composeAlt(
applyMiddleware(thunk, logger)
)
const store = createStore(
pokemonsReducer,
composeEnhancers
)
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<React.StrictMode>
<Provider store={store}>
<App />
</Provider>
</React.StrictMode>
)
Creación action creator
una vez integrado procederemos a crear nuestro action creator que será una función la cual contendrá la petición a la API que veníamos trabajando, esa función hará la petición a la API, obtendrá su respuesta y devolverá otra función la cual recibe como parámetro el dispatch para poder disparar acciones, este dispatch recibirá como parámetro la acción setPokemons que a su vez recibirá como parámetro la respuesta de la petición a la API
src/actions/index.js:
export const getPokemonsWithDetails = (pokemons = [])=> async (dispatch)=> {
const pokemonsDetailed = await Promise.all(pokemons.map(pokemon => getPokemonDetail(pokemon)))
dispatch(setPokemons(pokemonsDetailed))
}
Separación de responsabilidades
una vez creada el action creator lo importamos y podemos hacer dispatch con el pasándole como parámetro nuestro arreglo de objetos de pokemones
src/App.js:
const App = () => {
const pokemons = useSelector(state => state.pokemons)
const dispatch = useDispatch()
useEffect(()=> {
const fetchPokemons = async ()=> {
const pokemonsRes = await getPokemon()
dispatch(getPokemonsWithDetails(pokemonsRes)) // **
}
fetchPokemons()
}, [])
return (
<div className="App">
....
</div>
);
}
Solución de Thunk con TypeScript en la v8.
.
// src/index.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { applyMiddleware, compose, legacy_createStore as createStore } from 'redux';
import thunk from 'redux-thunk';
import { Provider } from 'react-redux';
import { pokemonsReducer } from './reducers/pokemons';
import { logger } from './middlewares';
import App from './App';
import './index.css';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
const composedEnhancers = compose(
applyMiddleware(thunk, logger),
(window as any).__REDUX_DEVTOOLS_EXTENSION__ && (window as any).__REDUX_DEVTOOLS_EXTENSION__(),
)
const store = createStore(pokemonsReducer, composedEnhancers);
root.render(
<Provider store={store}>
<React.StrictMode>
<App />
</React.StrictMode>
</Provider>
);
.
// src/api/index.ts
import axios from "axios";
import { IPokemonDetails, IPokemonType } from "../types";
import { PokemonResponseType } from "./responses";
export const getPokemon = async () => {
const URL = "https://pokeapi.co/api/v2/pokemon?limit=151";
try {
const { data } = await axios.get<PokemonResponseType>(URL);
const { results } = data;
return results;
} catch (err) {
console.error("err ".repeat(5), err);
}
};
export const getPokemonDetails = async (pokemon: IPokemonType) => {
try {
const { data } = await axios.get<IPokemonDetails>(pokemon.url);
console.log(data)
return data;
} catch (err) {
console.error("err ".repeat(5), err);
}
};
.
// src/actions/index.ts
import { IPokemonDetails, IPokemonType } from "../types";
import { Dispatch } from "redux";
import { SET_POKEMONS_DETAILS } from "./types";
import { getPokemonDetails } from "../api";
export const setPokemonsDetails = (
payload: (IPokemonDetails | undefined)[]
) => ({
type: SET_POKEMONS_DETAILS,
payload,
});
export const getPokemonsDetailsAction =
(pokemons: IPokemonType[]) =>
// Dispatch se debe tipar para que no ocurran inconvenientes con lo que devuelve
async (dispatch: Dispatch<any>) => {
const pokemonDetails = await Promise.all(
pokemons.map((pokemon) => pokemon && getPokemonDetails(pokemon))
);
dispatch(setPokemonsDetails(pokemonDetails));
};
.
// src/App.tsx
import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Col } from "antd";
import Searcher from "./components/Searcher";
import PokemonList from "./components/PokemonList";
import { getPokemon } from "./api";
import { getPokemonsDetailsAction } from "./actions";
import { IPokemonDetails } from "./types";
import logo from "./statics/logo.svg";
import "./App.css";
import { Dispatch } from "redux";
type AppType = {
pokemons: IPokemonDetails[];
};
function App() {
const pokemons = useSelector((state: AppType) => state.pokemons);
// Dispatch se debe tipar para que no ocurran inconvenientes con lo que devuelve
const dispatch = useDispatch<Dispatch<any>>();
useEffect(() => {
const fetchPokemons = async () => {
const response = await getPokemon();
response && dispatch(getPokemonsDetailsAction(response));
};
fetchPokemons();
}, [dispatch]);
return (
<div className="App">
<Col span={4} offset={10}>
<img src={logo} alt="Pokedux" />
</Col>
<Col span={8} offset={8}>
<Searcher />
</Col>
<PokemonList pokemons={pokemons} />
</div>
);
}
export default App;
Por si alguno le ha ocurriod este error o uno similar por usar custom middlewares
Uncaught (in promise) TypeError: Cannot read properties of undefined (reading ‘payload’)
Debugeando un poco me di cuenta que las custom middlewares que el payload vienen con ua estructura diferente cuando se ingresa el compose de esta manera con la libreria thunk, ya que no trae la propiedad action dentro del objeto como lo hacia en clases anteriores, por el contrario trae las propiedades type y payload a primer nivel, por lo que corrigiendo esa logica dentro del middelware se corrigio mi problema.
Este Middelware antes lo tenia asi con la propiedad actionInfo.action.payload
export const addNumberToName = (store) => (next) => (actionInfo) => {
const featured = [
...actionInfo.action.payload.map((pokemon, index) => ({
...pokemon,
name: `${index + 1} - ${pokemon.name}`,
})),
]
const updateActionInfo = {
...actionInfo,
action: { ...actionInfo.action, payload: featured },
}
next(updateActionInfo)
}
Y modificandolo quedo así actionInfo.payload
export const addNumberToName = (store) => (next) => (actionInfo) => {
const featured = [
...actionInfo.payload.map((pokemon, index) => ({
...pokemon,
name: `${index + 1} - ${pokemon.name}`,
})),
]
const updateActionInfo = {
...actionInfo,
payload: featured,
}
next(updateActionInfo)
}
React custom hooks es mejor para data fetching que redux thunk.
https://redux.js.org/style-guide/#use-thunks-for-async-logic
Redux Thunk es un middleware de Redux que permite a los desarrolladores escribir acciones que retornan funciones en lugar de objetos. Estas funciones pueden realizar operaciones asíncronas, como llamadas a una API, y después dispatch acciones cuando se han completado dichas operaciones. De esta manera, Redux Thunk permite gestionar la lógica de la aplicación asíncrona dentro de las acciones de Redux, lo que puede mejorar la organización y claridad del código.
Redux es como lo era Vuex para Vue.js, un despelote total, luego evolucionó a Pinia y bum, un cambio de enfoque muy simple. Redux se está tardando en hacer esto y se puede quedar atrás respecto a otras alternativas
Habla mal de platzi que ya no tenga un curso de programación funcional.
¿Quieres ver más aportes, preguntas y respuestas de la comunidad?