2
votes

I'm using nextjs and react-apollo (with hooks). I am trying to update the user object in the apollo cache after a mutation (I don't want to refetch). What is happening is that the user seems to be getting updated in the cache just fine but the user object that the component uses is not getting updated. Here is the relevant code:

The page:

// pages/index.js

...

const Page = ({ user }) => {
  return <MyPage user={user} />;
};


Page.getInitialProps = async (context) => {
  const { apolloClient } = context;
  const user = await apolloClient.query({ query: GetUser }).then(({ data: { user } }) => user);

  return { user };
};

export default Page;

And the component:

// components/MyPage.jsx

...

export default ({ user }) => {
  const [toggleActive] = useMutation(ToggleActive, {
    variables: { id: user.id },
    update: proxy => {
      const currentData = proxy.readQuery({ query: GetUser });

      if (!currentData || !currentData.user) {
        return;
      }

      console.log('user active in update:', currentData.user.isActive);

      proxy.writeQuery({
        query: GetUser,
        data: {
          ...currentData,
          user: {
            ...currentData.user,
            isActive: !currentData.user.isActive
          }
        }
      });
    }
  });

  console.log('user active status:', user.isActive);

  return <button onClick={toggleActive}>Toggle active</button>;
};

When I continuously press the button, the console log in the update function shows the user active status as flipping back and forth, so it seems that the apollo cache is getting updated properly. However, the console log in the component always shows the same status value.

I don't see this problem happening with any other apollo cache updates that I'm doing where the data object that the component uses is acquired in the component using the useQuery hook (i.e. not from a query in getInitialProps).

Note that my ssr setup for apollo is very similar to the official nextjs example: https://github.com/zeit/next.js/tree/canary/examples/with-apollo

1

1 Answers

2
votes

The issue is that you're calling the client's query method. This method simply makes a request to the server and returns a Promise that resolves to the response. So getInitialProps is called before the page is rendered, query is called, the Promise resolves and you pass the resulting user object down to your page component as a prop. An update to your cache will not trigger getInitialProps to be ran again (although I believe navigating away and navigating back should), so the user prop will never change after the initial render.

If you want to subscribe to changes in your cache, instead of using the query method and getInitialProps, you should use the useQuery hook. You could also use the Query component or the graphql HOC to the same effect, although both of these are now deprecated in favor of the new hooks API.

export default () => {
  const { data: { user } = {} } = useQuery(GetUser)
  const [toggleActive] = useMutation(ToggleActive, { ... })

  ...
})

The getDataFromTree method (combined with setting the initial cache state) used in the boilerplate code ensures that any queries fetched for your page with the useQuery hook are ran before the page render, added to your cache and used for the actual server-side rendering.

useQuery utilizes the client's watchQuery method to create an observable which updates on changes to the cache. As a result, after the component is initially rendered server-side, any changes to the cache on the client-side will trigger a rerender of the component.