No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Agreguemos un loader

15/22
Recursos

Aportes 9

Preguntas 0

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

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
    }
}
para los que este utilizando **tailwindCss**, tambien existe un **ant Desinger** para este <https://daisyui.com/>
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