Jorge Zerpa
Mariangelica Useche
Team PlatziFranco Figueroa Flores
Carlos Eduardo Bracho Ramírez
Carlos Eduardo Bracho Ramírez
Joseph Humberto Cueva
Jesús Manuel Alarcón Maldonado
Engel Reyes
Joseph Humberto Cueva
Alex Leonardo Rodriguez Carvajal
Elam Vera
Mariangelica Useche
Team PlatziBárbaro Javier Valmaseda Vázquez
César Palma
Genessis Jimenez
Andrés David Solarte Vidal
Mariangelica Useche
Team PlatziNicolas Forero Quevedo
Mariangelica Useche
Team PlatziFranco Subiris
Franco Subiris
Mariangelica Useche
Team PlatziLuis Eduardo Martínez Espinoza
Mariangelica Useche
Team PlatziAlejandro José Hugo Escalante Santos
Ayelén Iasich
Daniel Alberto Vega Bejarano
Harold Zurita Simon
brandon james grimaldo moscote
Mariangelica Useche
Team PlatziDANIEL STEVEN RODRIGUEZ VERANO
Rubén Ernesto Aragón Gil
AMÉ. ESTE. CURSO. lo pondré en práctica ya mismo en un proyecto que estoy haciendo para mi portafolio💚💚💚
¡Genial! Me emociona mucho que te haya gustado 🤩🤩. No olvides dejar tu review ⭐⭐⭐⭐⭐ y nunca para de aprender 💚
Muchas gracias por el curso, excelente!
Para el buscador modifiqué el initialState agregándole un campo llamado pokemonsFiltered. También modifiqué el dataSlice, donde en setPokemons se cargan ambos campos y en setFavorite se lee pokemonsFiltered. Y agregué la función setFiltered
const initialState = { pokemons: [], pokemonsFiltered: [], }; /*..... */ export const dataSlice = createSlice({ name: "data", initialState, reducers: { setPokemons: (state, action) => { state.pokemons = action.payload; state.pokemonsFiltered = action.payload; }, setFavorite: (state, action) => { const currentPokemonIndex = state.pokemonsFiltered.findIndex( (pokemon) => pokemon.id === action.payload.pokemonId ); if (currentPokemonIndex >= 0) { const isFavorite = state.pokemonsFiltered[currentPokemonIndex].favorite; state.pokemonsFiltered[currentPokemonIndex].favorite = !isFavorite; } }, setFilter: (state, action) => { const pokemonsFiltered = state .pokemons .filter( pokemon => pokemon.name.includes( action.payload ) ) state.pokemonsFiltered = pokemonsFiltered; } }, });
El componente App lee pokemonsFiltered
const pokemons = useSelector((state) => state.data.pokemonsFiltered, shallowEqual);
Finalmente, el componente Searcher dispara la función setFilter para modificar la lista.
export const Searcher = () => { const dispatch = useDispatch(); const handleOnChange = (e) => { dispatch(setFilter(e.target.value)); } return <Input.Search placeholder="Buscar..." onChange={handleOnChange} style={{marginBottom: '10px'}} /> }
Estuve revisando las soluciones y la tuya es la más óptima. Es la que mejor sintetiza el código y separa UI de la lógica.
Solo una cosa: con tu método, cuando filtras Pokémons, se pierden los favoritos. Eso es porque setFilter determina filteredPokemons mediante state.pokemons.filter, es decir, reinicia el valor de filteredPokemons (que contiene los favoritos, y entonces los pierde) y reasigna al filtrar pokemons.
Debe haber una solución más elegante, pero por lo pronto, en setFavorites repetí el proceso para asignar favoritos tanto a pokemons como filteredPokemons:
setFavorite: (state, action) => { const currentPokemonIndex = state.pokemons.findIndex(pokemon => { return pokemon.id === action.payload.pokemonId }) const currentFilteredPokemonIndex = state.filteredPokemons.findIndex(pokemon => { return pokemon.id === action.payload.pokemonId }) if (currentPokemonIndex >= 0) { const isFavorite = state.pokemons[currentPokemonIndex].favorite state.pokemons[currentPokemonIndex].favorite = !isFavorite } if (currentFilteredPokemonIndex >= 0) { const isFavorite = state.filteredPokemons[currentFilteredPokemonIndex].favorite state.filteredPokemons[currentFilteredPokemonIndex].favorite = !isFavorite } },
Comparto el resultado del proyecto final: Repositorio: https://github.com/jhcueva/Pokedex Web desplegada: https://pokedex-tau.vercel.app
¡Que bonito!
Gran curso, rapido y conciso, que gran facilidad para explicar las cosas ❤ esta es mi pokedex construida con react y tailwind todavia no la termino, porque estaba con el tema del diseño, ya que soy pesimo en eso xD, pero implemente una paginación simple :) , modo oscuro 🌗 y un spinner de pokeball simulando la captura 😜
más adelante implementaré rutras (wouter), detallles del pokemon y fetchs por generaciones
!WebScreenshot
la captura de pantalla la realice con esta app :) WebScreenshots en esta utilize jotai para el manejo del estado :D
Las imagenes de los pokemon las sacaste de la misma api del curso?
Que importante es conocer el flujo de redux en versiones anteriores o "the old way", he aplicado redux toolkit en trabajo pero nunca la manera anterior a este. Muchas gracias por el curso!
Me fascino este curso! siempre le temo a React sin embargo ya estoy pronto de acabar la ruta con pasos firmes en mi aprendizaje.
Me encanta que te haya gustado el curso <3. La constancia es la clave 👏🏽 Felicidades.
¡Qué bien me encantó este curso!!
Tomó su tiempo, la ultima parte, pero finalmente se consiguió. Quería mas de una vez hacerlo sin redux por lo pequeño de la app :D
Excelente curso! Hace falta un curso de Redux Toolkit puramente, donde profundicemos mas en su uso.
Por fin entendi Redux.
Gracias!!!
¡Super, Andrés! Me alegra que lo hayas entendido. No olvides dejar tu review ⭐⭐⭐⭐⭐ y nunca pares de aprender 💚
estuvo buenisimo el curso
¡Gracias, Nicolás! Me alegra mucho que te haya gustado
Dejo mi solucion con estilos propios y el buscador funcional. Lo hice creando un nuevo metodo en el slice de data mas otro estado que verifica el estado del imput y en base a eso actualiza la UI en caso que no encuentre ningun resultado
web deployed: https://pokedux-delegadodev.netlify.app/ codigo: https://github.com/Delegado007/pokedux/tree/styles_component
mejoré un poo la performance destructurando el objeto que venia de resp de la API y asi solo guardar en el storage los datos relevantes:
let pokemones = [] pokemonsDetailed.map((elem) => { let { name, id, types, sprites: { front_default } } = elem; pokemones.push({ name, id, types, image: front_default }); })
¡Está increíble! Te felicito 👏🏽
Así quedó mi sitio con la función de buscar
¡Genial, Luis! Felicidades 👏🏽 ¿Qué fue lo que más te costó?
Excelente curso.
Excelente curso! Comparto mi versión de la app con algunas mejoras que implementé:
localStorage, lo que permite mantener los datos al recargar la página.Link al deploy:
Repositorio en GitHub:
Muchas gracias por el curso, fue bueno aunque hay cosas por mejorar como no saltar partes, es excelente abordando lo básico a lo avanzando, pasando por varios métodos de resolver, thanks Team Platzi
Muy buen curso 😃!
Solución al reto:
📝 Filtrado de Pokémons con Redux Toolkit
1️⃣ Estado y slice
En nuestro dataSlice definimos:
pokemons: lista de todos los pokémons. 🐱👤searchTerm: término de búsqueda para filtrar. 🔍También tenemos un reducer llamado setSearchTerm para actualizar este valor.
const initialState = { pokemons: [], searchTerm: "", };
Reducers principales
setPokemons: actualiza la lista de pokémons. 📝setFavorite: alterna el estado de favorito de un pokémon. ⭐setSearchTerm: actualiza el searchTerm. 🔍reducers: { setPokemons: (state, action) => { state.pokemons = action.payload; }, setFavorite: (state, action) => { const idx = state.pokemons.findIndex(p => p.id === action.payload.pokemonId); if (idx >= 0) state.pokemons[idx].favorite = !state.pokemons[idx].favorite; }, setSearchTerm: (state, action) => { state.searchTerm = action.payload; }, }
2️⃣ Filtrado de pokémons
Creamos un selector para filtrar los pokémons según el searchTerm:
export const selectFilteredPokemons = (state) => state.data.pokemons.filter((p) => p.name.toLowerCase().includes(state.data.searchTerm.toLowerCase()) );
✅ Esto permite que el filtrado sea reactivo, cada vez que el usuario escribe algo en el input.
3️⃣ Async Thunk para obtener detalles
Usamos createAsyncThunk para traer los pokémons y sus detalles desde la API:
export const fetchPokemonsWithDetails = createAsyncThunk( "data/fetchPokemonsWithDetails", async (_, { dispatch }) => { dispatch(setLoading(true)); // ⏳ const pokemonsRes = await getPokemon(); const pokemonsDetailed = await Promise.all( pokemonsRes.map((pokemon) => getPokemonDetails(pokemon)) ); dispatch(setPokemons(pokemonsDetailed)); dispatch(setLoading(false)); // ✅ } );
4️⃣ Uso en App.js
useSelector(selectFilteredPokemons)PokemonListconst filteredPokemons = useSelector(selectFilteredPokemons); {loading ? ( <Col offset={12}> <Spin spinning size="large" /> </Col> ) : ( <PokemonList pokemons={filteredPokemons} /> )}
5️⃣ Componente Searcher
searchTerm.<Input.Search placeholder="Buscar..." style={{ marginBottom: 16 }} value={searchTerm} onChange={(e) => handleSearch(e.target.value)} // ✍️ al escribir onSearch={handleSearch} // 🔍 al presionar Enter o click allowClear />
Este curso estuvo buenísimo, sin duda alguna lo mejor de lo mejor, muchas gracias profe.
¡Me encanta que te haya gustado, Brandon! No olvides dejar tu review <3
entendí como funciona Redux pero no se como implementarlo en mis proyectos de trabajo FreeLancer
Buen cursoo Gracias profa.