No tienes acceso a esta clase

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

No se trata de lo que quieres comprar, sino de quién quieres ser. Aprovecha el precio especial.

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

15 Días
23 Hrs
13 Min
52 Seg

Agreguemos favoritos

16/22
Recursos

Aportes 18

Preguntas 5

Ordenar por:

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

En el video creo que hay un corte, donde pasan la prop: favorite al componente PokemonCard, y en este se lo pasan al Card, se lo pasan al isFavorite, este prop le llega a PokemonCard desde PokemonList como favorite={pokemon.favorite}

import { useDispatch } from 'react-redux';
import { Card } from 'antd';
import Meta  from 'antd/lib/card/Meta';
import StarButton from './StarButton';
import { setFavorite } from '../actions/index'

const PokemonCard = ({ name, image, abilities, id, favorite }) => {
const dispatch = useDispatch();
const allAbilities = abilities.map(ability => ability.ability.name).join(', ');

const handleOnFavorite = () => {
    dispatch(setFavorite({pokemonId: id}));
}
    return (
    <Card
        title={name}
        cover={<img src={image} alt={name} />}
        extra={<StarButton isFavorite={favorite} onClick = {handleOnFavorite}/>}
    >
        <Meta description={allAbilities}/>
    </Card>)
}

export default PokemonCard;

en el minuto 15:41 al parecer hay un pequeño corte, si al terminar la clase falla o se rompe la aplicación, lo único que debemos hacer es pasar favorite como props desde el componente PokemonList al componente PokemonCard

const PokemonList = ({ pokemons })=> {
    console.log(pokemons)


    return (
        <div className="PokemonList">
            {
                pokemons.map(pokemon => (
                    <PokemonCard 
                        name={pokemon.name} 
                        key={pokemon.name} 
                        image={pokemon.sprites.front_default} 
                        types={pokemon.types} 
                        id={pokemon.id}
                        favorite={pokemon.favorite} // **
                    />
                ))
            }
        </div>
    )
}

luego debemos recibir esa prop en el componente PokemonCard y pasarla como valor de la prop isFavorite que PokemonCard le pasa a el componente StarButton

const PokemonCard = ({ name, image, types, id, favorite })=> {
    const dispatch = useDispatch()

    const handleOnFavorite = ()=> {
        dispatch(setFavorite({ pokemonId: id}))
    }

    return (
        <Card 
            ..... 
            extra={<StarButton isFavorite={favorite} onClick={handleOnFavorite} />}
        >
        </Card>
    )
}

de esta forma la aplicación no se romperá y tendríamos el código igual al de la clase, debemos saber que la API no contiene esta propiedad en un inicio, cada objeto pokemon no contiene esta propiedad favorite por defecto, pero el código funciona y la razón es que, en un inicio cuando pasamos la prop favorite desde el componente PokemonList al componente PokemonCard, el valor de esta prop es undefined, no esta definido aun, cuando la recibimos en PokemonCard y pasamos como valor de la prop isFavorite a el componente StarButton sigue siendo undefined, este componente StarButton la recibe y hace una validación con esta propiedad isFavorite, al ser su valor undefined renderizara el componente StarOutLined

const StarButton = ({ isFavorite, onClick })=> {
    const Icon = isFavorite ? StarFilled : StarOutlined
    
    return <Button icon={<Icon/>} onClick={onClick}>

    </Button>
}

esta propiedad favorite se añade a los objetos pokemon cuando se dispara la acción setFavorite y llega al reducer

export const pokemonsReducer = (state = initialState, action)=> {
    switch(action.type){
        case SET_POKEMONS:
            .... 
        case SET_FAVORITE:
            const newPokemonList = [ ...state.pokemons ]
            const pokemonIndex = newPokemonList.findIndex(pokemon => pokemon.id === action.payload.pokemonId)

            if (pokemonIndex < 0){
                return state
            }

            newPokemonList[pokemonIndex].favorite = !newPokemonList[pokemonIndex].favorite 
            return { ...state, pokemons: newPokemonList }
        case SET_LOADING:
                ....
        default: 
            return state 
    }
}

este caso del reducer lo que hace es crear la nueva propiedad favorite en el objeto pokemon y darle como valor lo opuesto al valor que tiene, como acabamos de crear la propiedad y no le hemos dado un valor aun, su valor por defecto es undefined y al darle como valor lo opuesto a undefined su valor al final seria true

console.log(!undefined) // true 

de esta manera hemos creado la propiedad favorite con su valor en true en el objeto pokemon al que demos click, y al dar click nuevamente su valor será lo opuesto, es decir de true a false y así cada vez que demos click. Analizando un poco el flujo de la data, pude notar que estaríamos creando esta propiedad solo en algunos objetos pokemon, al final tendriamos objetos pokemon que tienen una propiedad favorite y otros que no, lo cual puede generarnos conflictos en un futuro, para evitar esto lo hice de otra forma, no paso la propiedad favorite como prop de PokemonList a PokemonCard

const PokemonList = ({ pokemons })=> {
    console.log(pokemons)


    return (
        <div className="PokemonList">
            {
                pokemons.map(pokemon => (
                    <PokemonCard 
                        name={pokemon.name} 
                        key={pokemon.name} 
                        image={pokemon.sprites.front_default} 
                        types={pokemon.types} 
                        id={pokemon.id}
                    />
                ))
            }
        </div>
    )
}

por lo tanto, no la recibo en el componente PokemonCard, lo que hago es crear un estado local con valor inicial en false

const [favoriteLocal, setFavoriteLocal] = useState(false)

se lo paso como valor de la prop isFavorite a el componente StarButton, este componente StarButton al recibir la acción de click dispara la acción setFavorite la cual no recibirá un objeto {pokemonId: id}, solamente id, y cambiara el valor del estado local a su opuesto con setFavoriteLocal

const PokemonCard = ({ name, image, types, id })=> {
    const dispatch = useDispatch()
    const [favoriteLocal, setFavoriteLocal] = useState(false)

    const handleOnFavorite = ()=> {
        dispatch(setFavorite(id))
        setFavoriteLocal(!favoriteLocal)
    }

    return (
        <Card 
            style={{width: 250}} 
            title={name} 
            cover={<img src={image} alt={name} />} 
            extra={<StarButton isFavorite={favoriteLocal} onClick={handleOnFavorite} />}
        >
            ....
        </Card>
    )
}

sabemos que el componente StarButton esta recibiendo esa prop y haciendo una validación con ella, cuando el valor de esta prop sea true renderizara el componente StarFilled, de esta manera solucionaríamos el problema del renderizado y al dar click en las card se estará llenando y vaciando la estrella, pero el cambio mas grande esta en la acción que se disparo cuando llega al reducer, debemos añadir una nueva propiedad favorite con valor de un array vacío al estado inicial

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

una vez añadida la nueva propiedad solo debemos modificar el caso SET_FAVORITE, lo que este caso hará será añadir el objeto pokemon al que demos click al array de favoritos, y en caso de que ya exista, lo eliminara del arreglo

export const pokemonsReducer = (state = initialState, action)=> {
    switch(action.type){
        case SET_POKEMONS:
            ....
        case SET_FAVORITE:
            const x = state.pokemons.find(element => element.id == action.payload)
            const element = state.favorite.find(element => element.id == action.payload)

            if (element === undefined){
                return { ...state, favorite: [...state.favorite, x ]} 
            } else {
                const filter = state.favorite.filter(elem => elem.id !== action.payload )
                return { ...state, favorite: [ ...filter ] }
         }
        case SET_LOADING:
                ....
        default: 
            return state 
    }
} 

de esta manera, al dar click sobre un card de un pokemon se añadirá ese objeto al arreglo de favoritos y su estrella se llenara, al dar click nuevamente se sacara del arreglo y su estrella se vaciara, al final tendríamos un arreglo de pokemons con todos nuestros objetos pokemon intactos y otro arreglo con solo nuestros objetos pokemon favoritos

Va quedando genial, a quién más le gustó ?

Problemas de rendimiento

Hasta este punto se ha creado este proyecto dejando de lado un tema muy importante: Performance.
Tal cual ha avanzado el proyecto hasta este punto, en cada momento en el que se agrega un pokemon como favorito, esto causa que toda nuestra lista de pokemones se renderice totalmente.

Si de toda la lista de pokemones (digamos que pueden ser miles), solo cambia el valor isFavorite de 1 solo. ¿Es coherente que se rendericen TODOS los pokemones?

Pues la respuesta es NO!!! Y aquí te dejo una pequeña solución.

Para esto utilizaremos React.memo(), esta función nos permite guardar en memoria el estado renderizado de un componente y hacer una evaluación que básicamente es lo siguiente: ¿Las props del componente cambiaron desde el último renderizado?

Si la respuesta es no, pues React no vuelve a renderizar el componente, tan solo muestra el que tenía guardado. Si la respuesta es sí, React vuelve a renderizar el componente.

Ojo React.memo tiene muchas mas implicaciones y te recomiendo leer la documentación para saber cuando utilizarlo.

Entonces, esto parece que soluciona nuestro problema.

Pues lo único que debemos hacer es envolver nuestro componente PokemonCard dentro de este HOC:

export default React.memo(PokemonCard, (prevProps, nextProps) => {
  return prevProps.isFavorite === nextProps.isFavorite
});

De esta forma evitaremos renderizados innecesarios y cuando se agregue un pokemon a favoritos, solo se re-renderizará ese card, no toda la lista.

Saludos,

luego de terminar el video, deben pasarle la prop favorite al pokemonCard.jsx desde el pokemonList.jsx

favorite={pokemon.favorite}

y luego del pokemonCard.jsx pasarle esa misma prop al StarButton.jsx

extra={<StarButton isFavorite={favorite} onClick={handleOnFavorite} />}

Se me ocurrio hacer el cambio de otra forma

case SET_FAVORITE:
      const pokemonsState = [...state.pokemons];
      const newValues = pokemonsState.map((element) =>
        element.id === action.payload
          ? { ...element, isFavorite: !element.isFavorite }
          : element
      );

      return { ...state, pokemons: newValues };

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

Dejo mi codigo del proyecto en TS con Vite:

// PokemonList.tsx
import { FC } from "react";
import { PokemonDetailType } from "../api";
import { PokemonCard } from "./PokemonCard";
import './PokemonList.css'

interface Props {
    pokemons: PokemonDetailType[]
}

const PokemonList:FC<Props> = ({ pokemons }) =>{
    return (
        <div className="PokemonList">
            {pokemons.map((pokemon,index)=>(
                <PokemonCard 
                    id={pokemon.id}
                    name={pokemon.name} 
                    types={pokemon.types} 
                    image={pokemon.sprites.other.home.front_default}  
                    key={pokemon.name} 
                    isFavorite={pokemon.favorite}
                />
            ))}
        </div>
    )
}

PokemonList.defaultProps = {
    pokemons: Array(10).fill('')
}

export {
    PokemonList
}
// PokemonCard.tsx
import { FC } from "react";
import { Card } from "antd";
import Meta from "antd/lib/card/Meta";
import { StarButton } from "./StarButton";
import { useDispatch, useSelector } from "react-redux";
import { setFavorite } from "../actions";

type Props = {
    name: string,
    image: string,
    types: any[],
    id: number,
    isFavorite: boolean
}

const PokemonCard:FC<Props> = ({name, image, types, id, isFavorite}) => {
    const dispath = useDispatch();
    return (
        <Card
            title={name}
            cover={<img src={image} alt={name} />}
            extra={<StarButton isFavorite={isFavorite} onClick={()=>{
                dispath(setFavorite({pokemonId: id}))
            }} />}
        >
            <Meta description={types.map(type=>type.type.name).join()} />
        </Card>
    )
}

export {
    PokemonCard
}
// StarButton.tsx
import { StarFilled, StarOutlined } from "@ant-design/icons";
import { Button } from "antd";
import { FC } from "react";

type Props = {
    isFavorite: boolean,
    onClick : ()=>void
}

const StarButton:FC<Props> = ({ isFavorite, onClick}) =>{
    const Icon = isFavorite?< StarFilled />:<StarOutlined />
    return <Button icon={Icon} onClick={onClick}></Button>
}

export {
    StarButton
}
// api/index.ts
import axios from "axios";

export type PokemonType = {
    name: string;
    url: string
}

export type PokemonDetailType = {
    id:number;
    name: string;
    image : string;
    types: any[];
    sprites: any;
    abilities: any;
    favorite: boolean
}

type GetPokemonsResponse = {
    results: PokemonType[]
}

type GetPokemonsDetailResponse = PokemonDetailType

export const getPokemons = async ()=>{
    try {
        const { data } = await axios.get<GetPokemonsResponse>('https://pokeapi.co/api/v2/pokemon?limit=151');
        return data.results;
    } catch (err) {
        console.error(err);
    }
}

export const getPokemonDetails = async (pokemon:PokemonType) => {
    const { data } = await axios.get<GetPokemonsDetailResponse>(pokemon.url);
    return data;
}
// actions/index.ts
import { getPokemonDetails, PokemonDetailType, PokemonType } from "../api";
import { SET_FAVORITE, 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 setFavorite = (payload:any)=>({
    type: SET_FAVORITE,
    payload
})

export const getPokemonsWithDetails = 
(pokemons:PokemonType[]=[]) => 
async (dispatch: any) =>{
    const pkmnsDetailed = await Promise.all((pokemons as PokemonType[]).map(pkmn=>getPokemonDetails(pkmn)))
    dispatch(setPokemons(pkmnsDetailed))
}
//reducers/pokemon.ts
import { SET_FAVORITE, 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_FAVORITE:
            const newPokemonList =  [...state.pokemons]
            const currentPokemonIndex = newPokemonList.findIndex(
                pkmn => {
                    return action.payload.pokemonId === pkmn.id  
                }
            )
            if(currentPokemonIndex < 0){
                return state;
            }
            newPokemonList[currentPokemonIndex].favorite = !newPokemonList[currentPokemonIndex].favorite
            return {...state, pokemons: newPokemonList}
        case SET_LOADING:
            return {...state, loading: action.payload}
        default:
            return state
    }

}

Hola Todos!

Hubo un corte en el minuto 15:41 y en este corte no agregaron tres lineas en los scripts de PokemonCard.jsx y pokemonList.jsx

  1. El primero es agregar nuestra prop “favorite” en nuestra Pokemon Card

En el video sale de la siguiente manera:

const PokemonCard = ({ name, image, types, id}) => {

Pero con el prop quedaria de la siguiente manera:

const PokemonCard = ({ name, image, types, id, favorite }) => {
  1. El Segundo es agregar “={favorite}” en el return de nuestra card
    Antes estaba de la siguiente manera:
extra={<StarButton isFavorite onClick={handleOnFavorite} />}

Pero con el cambio quedaria de la siguiente manera:

extra={<StarButton isFavorite={favorite} onClick={handleOnFavorite} />}
  1. El ultimo esta en nuestro script pokemonList.jsx:
    Anteriormente el codigo estaba asi
const PokemonList = ({ pokemons }) => {
  return (
    <div className='PokemonList'>
      {pokemons.map((pokemon) => {
        return <PokemonCard 
        name={pokemon.name} 
        key={pokemon.name} 
        image={pokemon.sprites.front_default} 
        types = {pokemon.types}
        id={pokemon.id}
        
        />;
      })}
    </div>
  );
};

Pero agregando agregariamos el favorite y el codigo quedaria de la siguiente manera:

const PokemonList = ({ pokemons }) => {
  return (
    <div className='PokemonList'>
      {pokemons.map((pokemon) => {
        return <PokemonCard 
        name={pokemon.name} 
        key={pokemon.name} 
        image={pokemon.sprites.front_default} 
        types = {pokemon.types}
        id={pokemon.id}
        favorite={pokemon.favorite}
        />;
      })}
    </div>
  );
};
no se si este metodo es mas sencillo a largo plazo. júzguelo ustedes ```js import React from 'react' import { FaRegStar, FaStar } from 'react-icons/fa' import { useDispatch, useSelector } from 'react-redux' import { setFavorite } from '../actions' function PokemonCard({ index, name, image, tipo, ability }) { const dispatch = useDispatch() const pokemons = useSelector((state) => state.pokemons) const isFavorit = pokemons.find((pokemon) => pokemon.id === index)?.favorite const handleOnFavorite = () => { dispatch(setFavorite({ pokemonId: index })) } return ( <> <section className=" grid grid-rows-4">
{isFavorit ? ( <FaStar className=" absolute right-0 h-8 w-8 fill-amber-400" onClick={handleOnFavorite} /> ) : ( <FaRegStar className=" absolute right-0 h-8 w-8" onClick={handleOnFavorite} /> )} ```
no se si este metodo es mas sencillo a largo plazo. júzguelo ustedes ```js import React from 'react' import { FaRegStar, FaStar } from 'react-icons/fa' import { useDispatch, useSelector } from 'react-redux' import { setFavorite } from '../actions' const dispatch = useDispatch() const pokemons = useSelector((state) => state.pokemons) const isFavorit = pokemons.find((pokemon) => pokemon.id === index)?.favorite const handleOnFavorite = () => { dispatch(setFavorite({ pokemonId: index })) } return ( <> <section className=" grid grid-rows-4">
{isFavorit ? ( <FaStar className=" absolute right-0 h-8 w-8 fill-amber-400" onClick={handleOnFavorite} /> ) : ( <FaRegStar className=" absolute right-0 h-8 w-8" onClick={handleOnFavorite} /> )} ```import React from 'react'import { FaRegStar, FaStar } from 'react-icons/fa'import { useDispatch, useSelector } from 'react-redux'import { setFavorite } from '../actions' function PokemonCard({ index, name, image, tipo, ability }) {  const typeToColorClass = {    bug: 'bg-lime-100 text-lime-900',    dark: 'bg-gray-200 text-white',    dragon: 'bg-indigo-200 text-indigo-900',    electric: 'bg-yellow-200 text-black',    fairy: 'bg-pink-200 text-pink-900',    fighting: 'bg-red-400 text-white',    fire: 'bg-red-200 text-red-800',    flying: 'bg-teal-100 text-teal-700',    ghost: 'bg-purple-300 text-white',    grass: 'bg-green-100 text-green-800',    ground: 'bg-amber-900 text-white',    ice: 'bg-cyan-100 text-cyan-900',    normal: 'bg-gray-300 text-black',    poison: 'bg-purple-200 text-purple-900',    psychic: 'bg-pink-200 text-pink-900',    rock: 'bg-slate-300 text-slate-900',    steel: 'bg-gray-400 text-black',    water: 'bg-blue-200 text-blue-900',  }   const dispatch = useDispatch()   const pokemons = useSelector((state) => state.pokemons)   const isFavorit = pokemons.find((pokemon) => pokemon.id === index)?.favorite   const handleOnFavorite = () => {    dispatch(setFavorite({ pokemonId: index }))  }   return (    <>      \<section className=" grid grid-rows-4">        \
          {isFavorit ? (            \<FaStar              className=" absolute right-0 h-8 w-8 fill-amber-400"              onClick={handleOnFavorite}            />          ) : (            \<FaRegStar              className=" absolute right-0 h-8 w-8"              onClick={handleOnFavorite}            />          )}
no se si este metodo es mas sencillo a largo plazo. júzguelo ustedes ```js import React from 'react' import { FaRegStar, FaStar } from 'react-icons/fa' import { useDispatch, useSelector } from 'react-redux' import { setFavorite } from '../actions' function PokemonCard({ index, name, image, tipo, ability }) { const typeToColorClass = { bug: 'bg-lime-100 text-lime-900', dark: 'bg-gray-200 text-white', dragon: 'bg-indigo-200 text-indigo-900', electric: 'bg-yellow-200 text-black', fairy: 'bg-pink-200 text-pink-900', fighting: 'bg-red-400 text-white', fire: 'bg-red-200 text-red-800', flying: 'bg-teal-100 text-teal-700', ghost: 'bg-purple-300 text-white', grass: 'bg-green-100 text-green-800', ground: 'bg-amber-900 text-white', ice: 'bg-cyan-100 text-cyan-900', normal: 'bg-gray-300 text-black', poison: 'bg-purple-200 text-purple-900', psychic: 'bg-pink-200 text-pink-900', rock: 'bg-slate-300 text-slate-900', steel: 'bg-gray-400 text-black', water: 'bg-blue-200 text-blue-900', } const dispatch = useDispatch() const pokemons = useSelector((state) => state.pokemons) const isFavorit = pokemons.find((pokemon) => pokemon.id === index)?.favorite const handleOnFavorite = () => { dispatch(setFavorite({ pokemonId: index })) } return ( <> <section className=" grid grid-rows-4">
{isFavorit ? ( <FaStar className=" absolute right-0 h-8 w-8 fill-amber-400" onClick={handleOnFavorite} /> ) : ( <FaRegStar className=" absolute right-0 h-8 w-8" onClick={handleOnFavorite} /> )} ```import React from 'react'import { FaRegStar, FaStar } from 'react-icons/fa'import { useDispatch, useSelector } from 'react-redux'import { setFavorite } from '../actions' function PokemonCard({ index, name, image, tipo, ability }) {  const typeToColorClass = {    bug: 'bg-lime-100 text-lime-900',    dark: 'bg-gray-200 text-white',    dragon: 'bg-indigo-200 text-indigo-900',    electric: 'bg-yellow-200 text-black',    fairy: 'bg-pink-200 text-pink-900',    fighting: 'bg-red-400 text-white',    fire: 'bg-red-200 text-red-800',    flying: 'bg-teal-100 text-teal-700',    ghost: 'bg-purple-300 text-white',    grass: 'bg-green-100 text-green-800',    ground: 'bg-amber-900 text-white',    ice: 'bg-cyan-100 text-cyan-900',    normal: 'bg-gray-300 text-black',    poison: 'bg-purple-200 text-purple-900',    psychic: 'bg-pink-200 text-pink-900',    rock: 'bg-slate-300 text-slate-900',    steel: 'bg-gray-400 text-black',    water: 'bg-blue-200 text-blue-900',  }   const dispatch = useDispatch()   const pokemons = useSelector((state) => state.pokemons)   const isFavorit = pokemons.find((pokemon) => pokemon.id === index)?.favorite   const handleOnFavorite = () => {    dispatch(setFavorite({ pokemonId: index }))  }   return (    <>      \<section className=" grid grid-rows-4">        \
          {isFavorit ? (            \<FaStar              className=" absolute right-0 h-8 w-8 fill-amber-400"              onClick={handleOnFavorite}            />          ) : (            \<FaRegStar              className=" absolute right-0 h-8 w-8"              onClick={handleOnFavorite}            />          )}
no se si este metodo es mas sencillo a largo plazo. júzguelo ustedes ` import React from 'react'import { ````js ````FaRegStar, FaStar } from 'react-icons/fa'import { useDispatch, useSelector } from 'react-redux'import { setFavorite } from '../actions'` `function PokemonCard({ index, name, image, tipo, ability }) {  const typeToColorClass = {    bug: 'bg-lime-100 text-lime-900',    dark: 'bg-gray-200 text-white',    dragon: 'bg-indigo-200 text-indigo-900',    electric: 'bg-yellow-200 text-black',    fairy: 'bg-pink-200 text-pink-900',    fighting: 'bg-red-400 text-white',    fire: 'bg-red-200 text-red-800',    flying: 'bg-teal-100 text-teal-700',    ghost: 'bg-purple-300 text-white',    grass: 'bg-green-100 text-green-800',    ground: 'bg-amber-900 text-white',    ice: 'bg-cyan-100 text-cyan-900',    normal: 'bg-gray-300 text-black',    poison: 'bg-purple-200 text-purple-900',    psychic: 'bg-pink-200 text-pink-900',    rock: 'bg-slate-300 text-slate-900',    steel: 'bg-gray-400 text-black',    water: 'bg-blue-200 text-blue-900',  }` `  const dispatch = useDispatch()` `  const pokemons = useSelector((state) => state.pokemons)` `  const isFavorit = pokemons.find((pokemon) => pokemon.id === index)?.favorite` `  const handleOnFavorite = () => {    dispatch(setFavorite({ pokemonId: index }))  }` `  return (    <>      <section className=" grid grid-rows-4">       
          {isFavorit ? (            <FaStar              className=" absolute right-0 h-8 w-8 fill-amber-400"              onClick={handleOnFavorite}            />          ) : (            <FaRegStar              className=" absolute right-0 h-8 w-8"              onClick={handleOnFavorite}            />          )}`
Yo lo he hecho de forma diferentes, a los `switch/cases`, me parece un poco más simple de llevar usando un poco de los conceptos que sé de Vuex, por lo que me hice un archivo aparte para las "`mutations`" que es lo que sería básicamente los favoritos ```js // reducer/mutations.js const markAsFavorite = (pokemonList, payload) => { const newPokemonList = [...pokemonList]; const currentPokemonIndex = newPokemonList.findIndex( (pokemon) => pokemon.id === payload.pokemonId, ); if (currentPokemonIndex < 0) { return pokemonList; } else { newPokemonList[currentPokemonIndex].favorite = !newPokemonList[currentPokemonIndex].favorite; return newPokemonList; } }; export { markAsFavorite }; ```Luego, esa función la llamo dentro de un ObjectMapper y queda tal que así: ```js // reducer/index.js import { markAsFavorite } from "./mutations"; const initialState = { loading: false, pokemons: [], }; const actionType = { setPokemon: "SET_POKEMON", setLoading: "SET_LOADING", setFavorite: "SET_FAVORITE", }; const reducerObject = (state, payload) => ({ [actionType.setPokemon]: { ...state, pokemons: payload, }, [actionType.setLoading]: { ...state, loading: payload, }, [actionType.setFavorite]: { ...state, pokemons: markAsFavorite(state.pokemons, payload), }, }); const reducer = (state = initialState, action) => reducerObject(state, action.payload)[action.type] || state; export { actionType, reducer }; ```Otra cosita que hice para lo del corte por fallos de edición fue meter el favorite dentro de la query ```js // api/index.js import axios from 'axios'; const API = 'https://pokeapi.co/api/v2/pokemon'; const getPokemon = () => axios .get(`${API}?limit=11&offset=0`) // Must be 151 .then((res) => res.data.results) .catch((error) => console.log(error)); const getDetails = (pokemon) => axios .get(pokemon.url) .then((res) => { return { ...res.data, favorite: false, // Esto soluciona el problema del corte }; }) .catch((error) => console.log(error)); export { getPokemon, getDetails }; ```Luego de eso fue un cambio chico en la pokedex y ya quedo ```jsx // components/Pokedex.jsx // ... const Pokedex = ({ pokemons }) => { let pokemonData; const pokeList = pokemons.map((pokemon, index) => { pokemonData = { // ... id: pokemon.id, favorite: pokemon.favorite, // ... }; return <PokeCard key={pokemon.name} pokemonData={pokemonData} />; }); return
{pokeList}
; }; // ... ```
No eran las habilidades? 👀 👀 👀 👀

Lo mejor para estos casos es tener un array de favoritos y persistir ese array en el localstorage.

La manera más sencilla de solucionar el problema del “corte” de la clase es agregando a todos los pokemons la propiedad favorite. En las actions, especificamente en getPokemonsWithDetails, pueden hacer lo siguiente:

export const getPokemonsWithDetails =
    (pokemons = []) =>
    async (dispatch) => {
        const pokeDetail = await Promise.all(
            pokemons.map((p) => getDetailedPokemons(p))
        );
        const pokeDetailWithFav = pokeDetail.map((p) => {
            p.favorite = false;
            return {
                ...p,
            };
        });
        dispatch(setPokemons(pokeDetailWithFav));
    };

Y así les quedaría la Card del Pokemon:

import { Card } from "antd";
import Meta from "antd/es/card/Meta";
import StarButton from "../StarButton/StarButton";
import { useDispatch } from "react-redux";
import { setFavorite } from "../../actions";
const PokeCard = ({ poke }) => {
    const dispatch = useDispatch();
    const capitalize = (word) => {
        return word.charAt(0).toUpperCase() + word.slice(1);
    };

    const pokeTypes = poke.types.map((t) => t.type);
    const typeNames = pokeTypes.map((t) => capitalize(t.name)).join(" - ");

    const handleClick = () => {
        dispatch(setFavorite({ pokeId: poke.id }));
    };
    return (
        <Card
            title={capitalize(poke.name)}
            cover={<img src={poke.sprites.front_default} alt={poke.name}></img>}
            extra={
                <StarButton isFavorite={poke.favorite} onClick={handleClick} />
            }
        >
            <Meta description={typeNames} />
        </Card>
    );
};

export default PokeCard;

Espero le sirva a alguien!

Cuando la profe hizo esto:

pokemonList[pokemonId].favorite = !pokemonList[pokemonId].favorite;

Dije: ¡¡Que trucazo!!

Yo había pensado agregar ids o nombres de pokemons en otro array donde se guardaran los favoritos para luego consultarlos en ese array según la lógica que quisiera hacer después, pero me gustó esta solución. En mi caso como uso un objecto reductor, mi lógica quedó así:

Esto es en el reducer.js

//Objeto reductor
const objectReducer = (state, payload) => ({
    [actionType.setPokemons] : {
        ...state,
        pokemons: payload,
    },
    [actionType.setLoading] : {
        ...state,
        loading: payload,
    },
    [actionType.setAddedToFavorites] : {
        ...state,
        pokemons: payload,
    }
})
//Función reductora la cual devuelve el estado actualizado
function pokemonsReducer(state = initialState, action){
    return objectReducer(state, action.payload)[action.type] || state;
}

Esto es en el componente del button

import React from 'react';
import { HiStar, HiOutlineStar } from "react-icons/hi2";
import { useDispatch, useSelector } from 'react-redux';
import { handleSetAddedToFavorite } from '../utils/pokemonRedux';

function FavoriteIcon({pokemon}){
    const pokemonState = useSelector(state => state.pokemons);
    const dispatch = useDispatch();
    const Icon = pokemon.favorite ? HiStar : HiOutlineStar;

    //Agregar o eliminar de favoritos
    const handleAddToFavorite = () => { 
        const pokemonList = [...pokemonState];
        const pokemonId = pokemonList.findIndex((item)=>{
             return item.id == pokemon.id;
        })
        pokemonList[pokemonId].favorite = !pokemonList[pokemonId].favorite;

        dispatch(handleSetAddedToFavorite(pokemonList))
    }

    return(
        <button className='pe-2' onClick={handleAddToFavorite}>
           <Icon/>
        </button>
    )
}
![](https://static.platzi.com/media/user_upload/image-46bd897b-bd38-4d8e-aae2-f950b0969808.jpg) esta parte del codigo no estoy muy segura si esta bien o sea funciona pero, cuando doy click a mi btn star demora en cambiar el color: ![](https://static.platzi.com/media/user_upload/image-e94cc4a8-e66a-4d21-a0f0-6815f2956ebe.jpg)