No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Redux Thunk

13/22
Recursos

Aportes 10

Preguntas 4

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

o inicia sesi贸n.

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;

Encontr茅 esta explicaci贸n de Redux Thunk que me ayud贸 a entederlo. Se los comparto.

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.

Quiero aclarar que redux pide que se use RTK para que no existan inconvenientes con el tipado del Disptach
Es esta la raz贸n de porque el tipado en mi action-thunk qued贸 de esta forma que comparto.

.

// 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 鈥榩ayload鈥)
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 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.

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.