Home > Mobile >  React ( Typescript) component not rerendering upon updating Context
React ( Typescript) component not rerendering upon updating Context

Time:04-26

I have a LaunchItem component which uses React.Context to get and set information to/from the local storage.

What I am trying to achieve is that, when the component updates the Context (and local storage), I want it to rerender with the new information, so that it then updates the state of a local button.

The problem is, although the Context seems to be updated as well as the contents of the local storage, the item is not rerendered. (when I refresh the page I can see the button has changed state, however, signifying that it is able to derive that information from the Context just fine.

I will now share some code and hopefully someone is able to understand what I might be missing, I thoroughly appreciate your help :)

Context provider setup

type FavoritesContextType = {
  favorites: Favorites;
  updateFavorites: (category: StorageCategory, item: string) => void;
};

export const FavoritesContext = createContext<FavoritesContextType>(
  {} as FavoritesContextType
);

const FavoritesProvider: FC = ({ children }) => {
  const [favorites, setFavorites] = useState<Favorites>(
    getFromLocalStorage(SOME_CONSTANT)
  );

  const updateFavorites = (category: StorageCategory, item: string) => {
    updateLocalStorage(category, item);
    setFavorites(favorites);
  };

  return (
    <FavoritesContext.Provider value={{ favorites, updateFavorites }}>
      {children}
    </FavoritesContext.Provider>
  );
};

export const useFavoritesContext = () => useContext(FavoritesContext);

App.tsx

export const App = () => {
  return (
    <FavoritesProvider>
      {/* Some routing wrapper and a few routes each rendering a component */}
      <Route path="/launches" element={<Launches />} />
    </FavoritesProvider>
  )

Launches.tsx

export const LaunchItem = ({ launch }: LaunchItemProps) => {
  const { favorites, updateFavorites } = useFavoritesContext();
  const [isFavorite, setIsFavorite] = useState(false);

  useEffect(() => {
    if (favorites) {
      setIsFavorite(
        favorites.launches.includes(launch.flight_number.toString())
      );
    }
  }, [favorites]);

  return (
    {/* The rest of the component, irrelevant */}
    <FavoriteButton
      isFavorite={isFavorite}
      updateFavorites={() => {
        updateFavorites(
          StorageCategory.Launches,
          launch.flight_number.toString()
          );
       }}
   />
  )

FavoriteButton.tsx

export const FavoriteButton = ({
  isFavorite,
  updateFavorites,
}: FavoriteButtonProps) => {
  const handleClick = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
    e.preventDefault();
    updateFavorites();
  };

  return (
    // Using Link vs a Button to be able to preventDefault of parent Link
    <Link
      onClick={handleClick}
    >
    {/* The rest of the component, irrelevant */}

CodePudding user response:

It seems as though in your updateFavorites function you're calling setFavorites and passing in the existing favorites value. Try instead writing your updateFavorites function as:

  const updateFavorites = (category: StorageCategory, item: string) => {
    updateLocalStorage(category, item);
    setFavorites(getFromLocalStorage(SOME_CONSTANT));
  };

There are other ways you could determine what value to pass to setFavorites but I reused your getFromLocalStorage function as I'm not sure how you're determining that state value.

By doing it this way you'll ensure that the value you're setting in setFavorites isn't the same as the existing favorites value and thus you'll trigger a re-render.

  • Related