Home > Blockchain >  How to create a global state in React Native?
How to create a global state in React Native?

Time:10-02

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:

  1. When the user likes the exercise:

enter image description here

  1. The exercise object is updated in the local storage and gets passed to Favorites Screen:

enter image description here

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 :)

  • Related