No tienes acceso a esta clase

¬°Contin√ļa aprendiendo! √önete y comienza a potenciar tu carrera

Compra acceso a todo Platzi por 1 a√Īo

Antes: $249

Currency
$209/a√Īo

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

15D
22H
59M
31S

Agreguemos un loader

15/22
Recursos

Aportes 8

Preguntas 0

Ordenar por:

¬ŅQuieres ver m√°s aportes, preguntas y respuestas de la comunidad?

o inicia sesión.

La cantidad de código que hay que escribir se ha incrementado mucho para algo sencillo. Pero seguro con la práctica y la repetición será más fácil de recordar los pasos a seguir.

Muy bueno el curso, pero no me gusto ant design, yo sigo usando tailwind pero como se menciono al inicio solo es para maquetacion r√°pida.

también podemos colocar el dispatch de la acción que controla el loading directamente en la acción getPokemonWithDetails

export const getPokemonsWithDetails = (pokemons = [])=> async (dispatch)=> {
    dispatch(setLoading(true))
    const pokemonsDetailed = await Promise.all(pokemons.map(pokemon => getPokemonDetail(pokemon)))
    dispatch(setPokemons(pokemonsDetailed))
    dispatch(setLoading(false))
}

de esta forma, la misma acción controla la petición a la API y la carga del spinner, que siempre van de la mano uno con el otro, y nuestro useEffect del componente App quedaría mucho mas limpio

const App = ()=> {
  ...

  useEffect(()=> {
    const fetchPokemons = async ()=> {
      const pokemonsRes = await getPokemon()
      dispatch(getPokemonsWithDetails(pokemonsRes))
    }

    fetchPokemons()
  }, [])
}

Mi practica

App.jsx

import {Col, Spin} from 'antd';
import Searcher from "./components/Searcher";
import {useEffect} from 'react'
import PokemonList from "./components/PokemonList.jsx";
import {getPokemon} from "./api";
import {getPokemonWithDetails, setLoadings} from "./actions/index.js";
import Logo from "./assets/logo.svg";
import './App.css'
import {useDispatch, useSelector} from "react-redux";

function App() {
    const pokemons = useSelector(state => state.pokemons);
    const loading = useSelector(state => state.loading);
    const dispatch = useDispatch();
    useEffect(() => {
        const fetchPokemons = async () => {
            dispatch(setLoadings(true))
            const response = await getPokemon();
            dispatch(getPokemonWithDetails(response));
            dispatch(setLoadings(false))
        }
        fetchPokemons();
    }, []);

    return (
        <div className="App">
            <Col span={4} offset={10}>
                <img src={Logo} alt="pokeapi"/>
            </Col>
            <Col span={8} offset={8}>
                <Searcher/>
            </Col>
            {loading ? (
                <Col offset={12} className="Spinner">
                    <Spin spinning size="large"/>
                </Col>
            ) : (
                <PokemonList pokemons={pokemons}/>
            )}

        </div>
    )
}

export default App;

App.css

@import "antd/dist/antd.css";

.App{
margin-top: 3rem;
}
.App img {
margin-bottom: 3rem;
}

.App img {
margin-bottom: 3rem;
}
.Spinner {
margin-top: 3rem;
}

reducers/pokemons.js

import {SET_LOADING,SET_POKEMON} from "../actions/types.js";

const initialState = {pokemons: [], loading: false};

export const pokemonsReducer = (state,action) => {
    switch (action.type) {
        caseSET_POKEMON:
            return {...state, pokemons:action.payload}
            break;
        caseSET_LOADING:
            return {...state, loading:action.payload}
            break;
        default:
            return {...state};
            break;
    }
}

actions/types.js

export constSET_POKEMON= 'SET_POKEMON';
export constSET_LOADING= 'SET_LOADING';

actions/index.js

import {SET_POKEMON,SET_LOADING} from "./types";
import {getPokemonDetails} from "../api";

export const setPokemons = (payload) => ({
    type:SET_POKEMON,
payload
});

export const setLoadings = (payload) => ({
    type:SET_LOADING,
payload,
});

export const getPokemonWithDetails = (pokemons= []) => async (dispatch) => {
    const pokemonsDetailed = awaitPromise.all(pokemons.map(pokemon=>getPokemonDetails(pokemon)));
dispatch(setPokemons(pokemonsDetailed));
};

Aporte en TypeScript de la clase

.

// src/actions/types.ts
export const SET_POKEMONS_DETAILS = "SET_POKEMONS_DETAILS";
export const SET_LOADING = "SET_LOADING";



// src/actions/index.ts

import { IPokemonDetails, IPokemonType } from "../types";
import { Dispatch } from "redux";
import { SET_LOADING, SET_POKEMONS_DETAILS } from "./types";
import { getPokemonDetails } from "../api";

export const setPokemonsDetails = (
  payload: (IPokemonDetails | undefined)[]
) => ({
  type: SET_POKEMONS_DETAILS,
  payload,
});

export const setLoading = (payload: boolean) => ({
  type: SET_LOADING,
  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/reducers/pokemons.ts

import { SET_LOADING, SET_POKEMONS_DETAILS } from "../actions/types";
import { InitialPokemonsStateType } from "../types";

const initialState: InitialPokemonsStateType = {
  pokemons: [],
  loading: false,
};

export const pokemonsReducer = (
  state: InitialPokemonsStateType = initialState,
  action: any
) => {
  switch (action.type) {
    case SET_POKEMONS_DETAILS:
      return {
        ...state,
        pokemons: action.payload,
      };

    case SET_LOADING:
      return {
        ...state,
        loading: action.payload,
      };

    default:
      return state;
  }
};

.

// src/App.tsx

import React, { useEffect } from "react";
import { useDispatch, useSelector } from "react-redux";
import { Col, Spin } from "antd";
import Searcher from "./components/Searcher";
import PokemonList from "./components/PokemonList";
import { getPokemon } from "./api";
import { getPokemonsDetailsAction, setLoading } from "./actions";
import { InitialPokemonsStateType, 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);
  const loading = useSelector(
    (state: InitialPokemonsStateType) => state.loading
  );

  // Dispatch se debe tipar para que no ocurran inconvenientes con lo que devuelve
  const dispatch = useDispatch<Dispatch<any>>();

  useEffect(() => {
    const fetchPokemons = async () => {
      dispatch(setLoading(true));

      const response = await getPokemon();
      response && dispatch(getPokemonsDetailsAction(response));

      dispatch(setLoading(false));
    };

    fetchPokemons();
  }, [dispatch]);

  return (
    <div className="App">
      <Col span={4} offset={10}>
        <img src={logo} alt="Pokedux" />
      </Col>
      <Col span={8} offset={8}>
        <Searcher />
      </Col>
      {loading ? (
        <Col span={8} offset={8} style={{ marginTop: "2rem" }}>
          <Spin spinning size="large" />
        </Col>
      ) : (
        <PokemonList pokemons={pokemons} />
      )}
    </div>
  );
}

export default App;

.

// src/types/index.ts

export interface IPokemonDetails {
  abilities:                [];
  name: string;
  sprites: SpritesType;

}

export interface IPokemonType {
  name: string;
  url: string;
}

export type InitialPokemonsStateType = {
  pokemons: IPokemonType[];
  loading: boolean;
};

export type SpritesType = {
  front_default: string;
};

Dejo mi código TS de la clase:

// App.tsx
import { useEffect } from 'react'
import { useDispatch, useSelector } from 'react-redux'
import { Search } from './components/Search'
import { PokemonList } from './components/PokemonList'
import { Col, Spin } from 'antd'
import { getPokemons } from './api'
import { getPokemonsWithDetails, setLoading, setPokemons } from './actions'
import { TypeState } from './reducers/pokemon'
import logo from './statics/logo.svg'
import './App.css'

function App() {
  const pokemons = useSelector((state:TypeState)=>state.pokemons)
  const loading = useSelector((state:TypeState)=>state.loading)
  const dispath = useDispatch();
  useEffect(()=>{
    async function fetchPokemon(){
        dispath(setLoading(true))
        const pkmns = await getPokemons();
        dispath(getPokemonsWithDetails(pkmns))
        dispath(setLoading(false))
    } 
    fetchPokemon();
  },[])
  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;

// action/type.ts
export const SET_POKEMONS = 'set_pokemons'
export const SET_LOADING = 'set_loading'
// action/index.ts
import { getPokemonDetails, PokemonDetailType, PokemonType } from "../api";
import { SET_LOADING, SET_POKEMONS } from "./types";


export const setPokemons = (payload: PokemonDetailType[])=>({
    type: SET_POKEMONS,
    payload
})

export const setLoading = (payload:boolean)=>({
    type: SET_LOADING,
    payload
})

export const getPokemonsWithDetails = 
(pokemons:PokemonType[]=[]) => 
async (dispatch: any) =>{
    const pkmnsDetailed = await Promise.all((pokemons as PokemonType[]).map(pkmn=>getPokemonDetails(pkmn)))
    dispatch(setPokemons(pkmnsDetailed))
}
import { SET_LOADING, SET_POKEMONS } from "../actions/types"
import { PokemonDetailType } from "../api"

export type TypeState = {
    pokemons: PokemonDetailType[],
    loading : boolean
}

const initialState: TypeState = {
    pokemons: [],
    loading: false
}

export const pokemonReducer = (state:TypeState = initialState, action:any)=>{
    switch(action.type){
        case SET_POKEMONS:
            return {...state, pokemons: action.payload}
        case SET_LOADING:
            return {...state, loading: action.payload}
        default:
            return state
    }
}
El loading de esa forma no es tan preciso, yo al momento de hacer el set\_pokemons en el reducer cambie el estado del loader a false con eso es m√°s preciso.

para una mejor experiencia de usuario considero que seria mejor un loader squeleton