Cómo guardar Pokémon favoritos con Redux

Resumen

Marcar Pokémon como favoritos en una app con Redux implica conectar tres piezas: un botón visual con Ant Design, un action creator que dispare el cambio y un reducer que actualice el estado de forma controlada. Aquí verás cómo encajan, paso a paso, usando un proyecto real de tarjetas de Pokémon.

Cómo mostrar los tipos de cada Pokémon dentro de la tarjeta

Antes de tocar el botón de favoritos, conviene saldar una deuda: los tipos estaban hardcodeados en el componente PokemonCard. La estructura real en Redux guarda los tipos como un arreglo, donde cada elemento tiene una propiedad type con un name adentro [01:00].

La solución es pasar pokemon.types desde PokemonList como prop y construir un string legible:

js const types = pokemon.types .map((element) => element.type.name) .join(', ');

El método map recorre el arreglo y extrae cada nombre. Después, join(', ') une esos nombres con coma y espacio, devolviendo un texto como grass, poison para Bulbasaur o fire para Charmeleon. Ese string se pasa como descripción al componente Card.Meta y los tipos se vuelven dinámicos al instante.

¿Para qué sirve el método join en JavaScript? Convierte un arreglo en un string uniendo cada elemento con el separador que le indiques. Si pasas ', ', obtienes elementos separados por coma y espacio.

Cómo crear un botón de favoritos con Ant Design

El componente Card de Ant Design expone una prop llamada extra, pensada para renderizar contenido en la esquina superior derecha de la tarjeta. Ese es el lugar perfecto para el botón de estrella [03:30].

La idea es construir un componente reutilizable, StarButton, que reciba dos props:

  • isFavorite: indica si ese Pokémon ya está marcado.
  • onClick: la función que se ejecuta al hacer clic.

Dentro del botón se elige el ícono según el estado:

js const icon = isFavorite ? <StarFilled /> : <StarOutlined />; return <Button icon={icon} onClick={onClick} />;

StarOutlined se usa cuando el Pokémon no es favorito y StarFilled cuando ya lo es, logrando ese efecto visual de estrella vacía contra estrella llena. Un detalle clave: no hay que invocar el componente dos veces (una al asignar la variable y otra al renderizar), porque eso rompe el árbol de React y llena la consola de errores.

¿Cómo conectar el botón con la tarjeta del Pokémon?

De vuelta en PokemonCard, el StarButton se pasa al prop extra del Card. Para validar que todo funciona antes de tocar Redux, basta con poner isFavorite={true} y un onClick con alert('clickeado'). Si la estrella se ve llena y la alerta dispara, la UI está lista para conectarse al estado global.

Cómo guardar el estado favorito en el reducer de Redux

Ahora viene la parte interesante: que el clic se traduzca en un cambio real del estado. Esto requiere tres piezas coordinadas, type, action creator y reducer, más un dispatch desde el componente [07:30].

Primero, el type:

js export const SET_FAVORITE = 'SET_FAVORITE';

Luego, el action creator que recibe un payload con el pokemonId:

js export const setFavorite = (payload) => ({ type: SET_FAVORITE, payload, });

En el reducer, el caso SET_FAVORITE hace cuatro cosas en orden:

  1. Crea una copia del arreglo de Pokémon con [...state.pokemons].
  2. Busca el índice del Pokémon usando findIndex y comparando pokemon.id === action.payload.pokemonId.
  3. Si el índice es menor a cero, retorna el estado sin cambios.
  4. Si existe, alterna la propiedad favorite con su negación, generando un toggle.

js case SET_FAVORITE: { const newPokemonList = [...state.pokemons]; const currentPokemonIndex = newPokemonList.findIndex( (pokemon) => pokemon.id === action.payload.pokemonId ); if (currentPokemonIndex < 0) return state; newPokemonList[currentPokemonIndex] = { ...newPokemonList[currentPokemonIndex], favorite: !newPokemonList[currentPokemonIndex].favorite, }; return { ...state, pokemons: newPokemonList }; }

¿Qué hace findIndex en un arreglo? Recorre los elementos y devuelve la posición del primero que cumple la condición. Si ninguno cumple, devuelve -1.

¿Por qué hay que copiar el arreglo antes de modificarlo?

Porque Redux exige inmutabilidad: nunca debes mutar el estado original. Crear una copia con el operador spread y modificar esa copia garantiza que React detecte el cambio y vuelva a renderizar. Este principio se profundiza en el siguiente módulo, pero por ahora alcanza con confiar en el patrón.

Cómo disparar la acción setFavorite desde el componente

El último paso es despachar la acción cuando el usuario haga clic. En PokemonCard se usa el hook useDispatch de react-redux y se crea una función handleOnFavorite:

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

Hay un detalle que suele tropezar: el payload debe enviarse como objeto con la propiedad pokemonId, no como valor suelto [12:30]. Si mandas solo el id, el reducer no encontrará la propiedad esperada y el estado no cambiará, aunque la acción sí se vea en Redux DevTools.

Para que esto funcione, PokemonList también debe pasar pokemon.id como prop al PokemonCard. Una vez conectado, hacer clic en la estrella alterna entre llena y vacía, y el estado global refleja qué Pokémon son favoritos.

¿Te animas a contar en los comentarios cuáles fueron los primeros tres Pokémon que marcaste como favoritos en tu propio proyecto?

      Cómo guardar Pokémon favoritos con Redux