Hope you're doing great :)
I'm working in a Favorite screen functionality, where I want to add the liked exercises to this screen. Everything went perfect, my problem is that the screens don't rerender when the button is clicked, only when the app is re-render. Here's the custom hook:
const useStorage = (name, id, pratice, navigation) => {
const [favorites, setFavorites] = useState();
const { storageDB } = useContext(StorageContext);
//Favorites Actions
const getFavorites = () => {
const checkFavorites = storageDB.map((store) =>
store.exercises.filter((exArray) => exArray.favorite)
);
const favoritesResult = checkFavorites.filter((ex) => ex.length !== 0);
if (favoritesResult.length !== 0) return setFavorites(favoritesResult);
return null;
};
const toggleFavorite = () => {
//Find exercise
const exerciseIndex = storageDB[topicIndex()].exercises.findIndex(
(ex) => ex.id === id
);
//Topic > Exercise
const exercise = storageDB[topicIndex()].exercises[exerciseIndex];
//Toogle favorite
exercise.favorite = !exercise.favorite;
//Store in AsyncStorage
storage.storeData(storageDB);
};
useEffect(() => {
getFavorites();
}, []);
return {
storageDB,
getFavorites,
toggleFavorite,
favorites
};
};
export default useStorage;
The hook params are meant to get the right exercise in the storage and gets called once in getFavorites().
Here's the FavoritesScreen:
const FavoritesScreen = () => {
const { favorites } = useStorage();
const Favorites = () => {
if (favorites)
return favorites.map((exArray) =>
exArray.map((ex) => (
<Card
key={ex.id}
exercise={ex}
topicName={ex.topicName}
onPress={() =>
navigation.navigate("ExerciseDetails", {
exercise,
name: ex.topicName
})
}
/>
))
);
return <NotFound text="No favorite exercises" noImage />;
};
return (
<Screen>
<Image source={require("../assets/favorites.png")} style={styles.image} />
<Title style={styles.title}>Favorites</Title>
<View style={styles.searchContainer}>
<SearchInput />
</View>
<Container scrollView>
<Favorites />
</Container>
</Screen>
);
};
I've tried so many things, used useEffect to listen to changes in the storageDB, even created a setStorageDB to access in all app, but nothing worked.
CodePudding user response:
its a little difficult to follow your code and understand exactly what you are trying to do, however the screen is only going to rerender if a state variable is reassigned. So favorites inside of useState will have to get reassigned to get a rerender.
For example toggleFavorite doesn't call setFavorites so it wont cause a screen to rerender, if you want the screen to rerender you will need to call setFavorites(newFavourites) where newFavorites has a different reference to the current favourites.
I hope this helps, usually I would include more code but as I said its a little tricky to work out exactly what you want, feel free to comment if you have further questions.
CodePudding user response:
The flow I intended was:
- When the user likes the exercise:
- The exercise object is updated in the local storage and gets passed to Favorites Screen:
My problem was that when the user unlikes the exercise, it didn't update both components (Happiness and Favorites). I've solved it using navigation.addListenter and creating a local state for Favorite Screen (as mentioned by Adam).
Here's the line in Favorites:
useEffect(() => {
navigation.addListener("focus", () => setFavorites(updatedFavorites()));
}, []);
Here's the line in Happiness:
useEffect(() => {
navigation.addListener("focus", () => setKey((prevKey) => prevKey 1));
}, []);
Now every time we enter these components, they will re-render if there're changes :)