Simplemente en la funcion onReloadCheckFavorite puede llamar al estado y negarlo, creo que esta forma es mas intuitiva.
const onReloadCheckFavorite = () => {
setReloadCheck(!reloadCheck);
}
Simplemente en la funcion onReloadCheckFavorite puede llamar al estado y negarlo, creo que esta forma es mas intuitiva.
const onReloadCheckFavorite = () => {
setReloadCheck(!reloadCheck);
}
Otra alternativa, podría ser en la misma función para sumar a los favoritos, negar al estado inicial de si es favorito o no (en mi caso isFavorites):
const addFavorites = async () => {
try {
await addFavorite(id);
setIsFavorites(!isFavorites)
} catch (err) {
console.log(err)
}
};
Mi código, el que creo que es más simplificado al del profesor y sin usar lodash:
FavButton.jsx:
import { useState, useLayoutEffect } from 'react'
import PropsTypes from 'prop-types'
import Icon from 'react-native-vector-icons/FontAwesome5'
import { addFavoritePokemon, removeFavoritePokemon, getFavoritePokemons } from '../../api/favStorage'
const FavButton = ({ id }) => {
const [ fav, setFav ] = useState(undefined)
useLayoutEffect(() => {
(async () => {
const favorites = await getFavoritePokemons()
const isFav = favorites.includes(id)
setFav(isFav)
})()
}, [])
const handleFavorite = () => {
fav
? removeFavoritePokemon(id)
: addFavoritePokemon(id)
setFav(!fav)
}
return (
<Icon
name='heart'
solid={fav && true}
color="#000"
size={24}
style={{ marginRight: 20 }}
onPress={handleFavorite}
/>
)
}
FavButton.propTypes = {
id: PropsTypes.number.isRequired
}
export default FavButton
favStorage.js
import AsyncStorage from '@react-native-async-storage/async-storage'
export const addFavoritePokemon = async (id) => {
try {
const favorites = await getFavoritePokemons()
favorites.push(id)
await AsyncStorage.setItem('favorites', JSON.stringify(favorites))
} catch (error) {
console.log(error)
}
}
export const removeFavoritePokemon = async (id) => {
try {
const favorites = await getFavoritePokemons()
const filterFavorites = favorites.filter(fav => fav !== id)
await AsyncStorage.setItem('favorites', JSON.stringify(filterFavorites))
} catch (error) {
console.log(error)
}
}
export const getFavoritePokemons = async () => {
try {
const response = await AsyncStorage.getItem('favorites')
return response != null ? JSON.parse(response) : []
} catch (error) {
console.log(error)
}
}
favorites: {
async getFavorites() {
try {
const response = await AsyncStorage.getItem(POKEMON_FAV_STORAGE);
if (response) {
let result = JSON.parse(response).filter(
(item, index) => JSON.parse(response).indexOf(item) === index
);
return result;
} else return [];
} catch (error) {
throw new Error(error);
}
},
async deleteFavorites(id) {
try {
const favorites = await this.getFavorites();
const filteredItems = favorites.filter((item) => item !== id);
await AsyncStorage.setItem(POKEMON_FAV_STORAGE, JSON.stringify(filteredItems));
return filteredItems;
} catch (error) {
throw new Error(error);
}
},
async addToFavorites(id) {
try {
const favorites = await this.getFavorites();
favorites.push(id);
await AsyncStorage.setItem(POKEMON_FAV_STORAGE, JSON.stringify(favorites));
return { message: 'Agregado correctamente a favoritos' };
} catch (error) {
throw new Error(error);
}
},
},
Con el onpress y dirigiéndolo a la función de cada caso se puede hacer esto sin necesidad de crear mas funciones, faltaría la lógica de borrarlo de la base de datos que seguramente es en las próximas clases
const addFavorites = async () => {
await addPokemonFavorite(id);
setIsFavorite(true);
};
const removeFavorites = () => {
console.log("eliminando");
setIsFavorite(false);
};
onPress={isFavorite ? removeFavorites : addFavorites}
Hola, la línea donde se asigna Icon:
const Icon = isFavorite ? FontAwesome : FontAwesome5;
¿se vuelve a ejecutar o porqué es que el ícono vuelve a cambiar si esta línea no está dentro del useEffect?. Gracias
Hay una opción muy sencilla que es usar props drilling, sube el estado [isFavorite y setIsFavorite] de orden al compente padre y le agregas otra dependencia al useEffect, luego si quieres optimizar renderizado usas memo en los demás componentes, para que no sea afecten, la otra es añadir los favoritos a un contexto y agregar a la dependencia los favoritos, cuando cambien tendrá que renderizar nuevamente.
Lo que yo hice fue modificar la función de addPokemonFavorite y verificar si existía el ID en los favoritos, si existe pues lo saco, sino le hago push y hago set del storage, así:
export async function addPokemonFavorite(id) {
try {
const favorites = await getFavorites();
favorites.includes(id) ? favorites.splice(favorites.indexOf(id), 1) : favorites.push(id);
await AsyncStorage.setItem(FAVORITES_STORAGE, JSON.stringify(favorites));
} catch (error) {
throw error;
}
}
Y en el botón de favorito lo que hice fue una función que chequea al ingresar si el pokemon es un favorito y luego setearlo
const Favorite = ({ id }) => {
const [isFav, setIsFav] = useState(false);
const addFavorite = async () => {
await addPokemonFavorite(id);
await isFavorite(id);
};
const isFavorite = async () => {
const isFavourite = await isPokemonFavorite(id);
setIsFav(isFavourite);
};
useEffect(() => {
isFavorite(id);
}, [id]);
return (
<>
<Icon
name='heart'
color='#FFF'
size={25}
onPress={addFavorite}
style={{ marginRight: 20 }}
solid={isFav}
/>
</>
);
};
export default Favorite;
Creo que el componente Favorite puede ser hecho de una mejor manera, pero está es bastante legible
Les comparto mi repo con los cambios que hice para esta clase:
Con el contexto de ese repo sólo tuve que hacer este cambio:
useEffect(() => {
fetchFavourites();
if (isPokemonSaved) {
setIcon(HeartIconEnum.Filled);
} else {
setIcon(HeartIconEnum.Outline);
}
}, [isPokemonSaved, icon, fetchFavourites]);
Luis Alejandro Nieto Ruth
Simplemente en la funcion onReloadCheckFavorite puede llamar al estado y negarlo, creo que esta forma es mas intuitiva.
const onReloadCheckFavorite = () => { setReloadCheck(!reloadCheck); }
Germán Edgardo Göhringer
Esa no es la mejor práctica. Es mejor la que hizo el profe.
Alejandro Chavez
¿Por qué no German? A mi me parece lo correcto, el mismo profesor dijo que no tendríamos el cuenta el valor de este estado, es solo para volver a montar el componente.
Rodrigo Alcino
Otra alternativa, podría ser en la misma función para sumar a los favoritos, negar al estado inicial de si es favorito o no (en mi caso isFavorites):
const addFavorites = async () => { try { await addFavorite(id); setIsFavorites(!isFavorites) } catch (err) { console.log(err) } };
Lucas Ojeda de Sousa
Eliezer Mas Y Rubi
Excelente!
Kevin Parra Lopez
Juan Camilo Osorio Bedoya
Con el onpress y dirigiéndolo a la función de cada caso se puede hacer esto sin necesidad de crear mas funciones, faltaría la lógica de borrarlo de la base de datos que seguramente es en las próximas clases
const addFavorites = async () => { await addPokemonFavorite(id); setIsFavorite(true); }; const removeFavorites = () => { console.log("eliminando"); setIsFavorite(false); }; onPress={isFavorite ? removeFavorites : addFavorites}
Oscar Uriel Cortés Zanabria
Hola, la línea donde se asigna Icon:
const Icon = isFavorite ? FontAwesome : FontAwesome5;
¿se vuelve a ejecutar o porqué es que el ícono vuelve a cambiar si esta línea no está dentro del useEffect?. Gracias
Agustín Navarro Galdon
ProfesorSi el componente se repinta todo se vuelve a ejecutar, de principio a final.
Daniel Ochoa
Hay una opción muy sencilla que es usar props drilling, sube el estado [isFavorite y setIsFavorite] de orden al compente padre y le agregas otra dependencia al useEffect, luego si quieres optimizar renderizado usas memo en los demás componentes, para que no sea afecten, la otra es añadir los favoritos a un contexto y agregar a la dependencia los favoritos, cuando cambien tendrá que renderizar nuevamente.
Sabino Fernández
David Rangel
Les comparto mi repo con los cambios que hice para esta clase:
Con el contexto de ese repo sólo tuve que hacer este cambio:
useEffect(() => { fetchFavourites(); if (isPokemonSaved) { setIcon(HeartIconEnum.Filled); } else { setIcon(HeartIconEnum.Outline); } }, [isPokemonSaved, icon, fetchFavourites]);
Mi código, el que creo que es más simplificado al del profesor y sin usar lodash:
FavButton.jsx:
import { useState, useLayoutEffect } from 'react' import PropsTypes from 'prop-types' import Icon from 'react-native-vector-icons/FontAwesome5' import { addFavoritePokemon, removeFavoritePokemon, getFavoritePokemons } from '../../api/favStorage' const FavButton = ({ id }) => { const [ fav, setFav ] = useState(undefined) useLayoutEffect(() => { (async () => { const favorites = await getFavoritePokemons() const isFav = favorites.includes(id) setFav(isFav) })() }, []) const handleFavorite = () => { fav ? removeFavoritePokemon(id) : addFavoritePokemon(id) setFav(!fav) } return ( <Icon name='heart' solid={fav && true} color="#000" size={24} style={{ marginRight: 20 }} onPress={handleFavorite} /> ) } FavButton.propTypes = { id: PropsTypes.number.isRequired } export default FavButton
favStorage.js
import AsyncStorage from '@react-native-async-storage/async-storage' export const addFavoritePokemon = async (id) => { try { const favorites = await getFavoritePokemons() favorites.push(id) await AsyncStorage.setItem('favorites', JSON.stringify(favorites)) } catch (error) { console.log(error) } } export const removeFavoritePokemon = async (id) => { try { const favorites = await getFavoritePokemons() const filterFavorites = favorites.filter(fav => fav !== id) await AsyncStorage.setItem('favorites', JSON.stringify(filterFavorites)) } catch (error) { console.log(error) } } export const getFavoritePokemons = async () => { try { const response = await AsyncStorage.getItem('favorites') return response != null ? JSON.parse(response) : [] } catch (error) { console.log(error) } }
Creo que se complejizo mucho el código, este es mi servicio
favorites: { async getFavorites() { try { const response = await AsyncStorage.getItem(POKEMON_FAV_STORAGE); if (response) { let result = JSON.parse(response).filter( (item, index) => JSON.parse(response).indexOf(item) === index ); return result; } else return []; } catch (error) { throw new Error(error); } }, async deleteFavorites(id) { try { const favorites = await this.getFavorites(); const filteredItems = favorites.filter((item) => item !== id); await AsyncStorage.setItem(POKEMON_FAV_STORAGE, JSON.stringify(filteredItems)); return filteredItems; } catch (error) { throw new Error(error); } }, async addToFavorites(id) { try { const favorites = await this.getFavorites(); favorites.push(id); await AsyncStorage.setItem(POKEMON_FAV_STORAGE, JSON.stringify(favorites)); return { message: 'Agregado correctamente a favoritos' }; } catch (error) { throw new Error(error); } }, },
Lo que yo hice fue modificar la función de addPokemonFavorite y verificar si existía el ID en los favoritos, si existe pues lo saco, sino le hago push y hago set del storage, así:
export async function addPokemonFavorite(id) { try { const favorites = await getFavorites(); favorites.includes(id) ? favorites.splice(favorites.indexOf(id), 1) : favorites.push(id); await AsyncStorage.setItem(FAVORITES_STORAGE, JSON.stringify(favorites)); } catch (error) { throw error; } }
Y en el botón de favorito lo que hice fue una función que chequea al ingresar si el pokemon es un favorito y luego setearlo
const Favorite = ({ id }) => { const [isFav, setIsFav] = useState(false); const addFavorite = async () => { await addPokemonFavorite(id); await isFavorite(id); }; const isFavorite = async () => { const isFavourite = await isPokemonFavorite(id); setIsFav(isFavourite); }; useEffect(() => { isFavorite(id); }, [id]); return ( <> <Icon name='heart' color='#FFF' size={25} onPress={addFavorite} style={{ marginRight: 20 }} solid={isFav} /> </> ); }; export default Favorite;
Creo que el componente Favorite puede ser hecho de una mejor manera, pero está es bastante legible