What are the differences between updating UI through `useState` and `cache.modify`?

I hope someone can explain the difference between updating through a useState and through cache.modify and why the one is preferred over the other, if that’s the case.

I have an input field where users can search for other users and they have the option to follow that user. I also have an option to unfollow that user:

method 1, useState

  const [usersFollowed, setFollowedUsers] = useState<IUser[]>([]);
  {error, loading, data: {returnFollowedUsers: followedUsers = {}} = {}},

  useEffect(() => {
    setFollowedUsers(followedUsers);
  }, [followedUsers]);

  const unfollowUser = async (user) => {
    await unfollowUserMutation({
      variables: {id: user.id},
    }).then((res) => {
      setFollowedUsers(res.data.unfollowUser);
    });
  };

I use the useEffect to populate the usersFollowed array when the component loads. And in the unfollowUser function I set the usersFollowed data with the response from the mutation.

But I can also do this by directly modifying the cache:

method 2, cache.modify

  {error, loading, data: {returnFollowedUsers: followedUsers = {}} = {}},

    const unfollowUser = async (user) => {
    await unfollowUserMutation({
      variables: {id: user.id},
      update: (cache, {data}) => {
        cache.modify({
          fields: {
            returnFollowedUsers: () => {
              return [...data.unfollowUser];
            },
          },
        });
      },
    });
  };

Is there a significant difference between the usecases? I’ve noticed that useState only updates the component it’s called in, while cache.modify seems to trigger updates in other components that have a reference to that cache

1 Like

This is correct! The big difference is that useState() only affects the local component, while cache.modify() will affect all components which are reading the same data. You will typically want to rely on cached query data rather than local component state for the purposes of consistency.

Before I was using reactive variables to store data in a global var and react to that in different components, but with cache the data is more consistent. Glad I’m not abusing cache then.

@brainkim quick question though, the mutation/query have to be done before the cache is available? I tried to work with my currentUser cache (works on a sign in mutation etc.) but on initial app load the currentUser cache was empty.

I’m not sure how you would be able to make queries/mutations before the cache is available?

One thing you can do is serialize an entire cache instance using cache.extract() (class InMemoryCache - Apollo GraphQL Docs) and cache.restore() (class InMemoryCache - Apollo GraphQL Docs). This is done sometimes when doing server-side rendering and hydration to prevent the client from making the same queries as was made on the server.

I forgot the difference between client and browser cache. The Apollo cache is created when the app is used and removed when the browser closes right?

Yes, although there are some options for persisting the cache (GitHub - apollographql/apollo-cache-persist: 🎏 Simple persistence for all Apollo Cache implementations) for instance.