Urql: How to manually (force) invalidate cache

Created on 13 Jun 2019  路  9Comments  路  Source: FormidableLabs/urql

I might be approaching this in the wrong way but I'm in the situation where, in my app, when a user logs out, I want to invalidate the entire cache.

Coming from Apollo I'd just call client.resetStore(). Urql's methodology of exchanges seems to work a bit differently and I can't see any such method on the client returned from createClient.

Is this somthing I'd be able to do but manually composing the exchanges and hooking into the cache exchange or I am approaching this in completely the wrong way?

Thanks in advance

Most helpful comment

So if anyone else is interested in how I did this here's an example:

Define a makeClient function. Mine looks like this.

const makeClient = () =>
  createClient({
    url: process.env.REACT_APP_API_URL || '',
    fetchOptions: () => {
      const token = store.get('auth_token');
      if (!token) {
        return {};
      }
      return {
        headers: {
          authorization: `Bearer ${token}`,
        },
      };
    },
  });

Create a context that should "house" the client. One of its props should be the makeClient function. You could just use the createClient function directly imported from URQL but this approach allows dependency injection so different clients can be used depending on the situation, as well as any app-specific configuration can be placed outside of this component.

const ClientContext = React.createContext<ClientState>({
  // this is just to satisfy the TS compiler. If you're using JS you can just omit the default value.
  resetClient: always(undefined),
});

function ClientProvider({ makeClient, children }: Props) {
  const [client, setClient] = React.useState(makeClient());

  return (
    <ClientContext.Provider
      value={{
        resetClient: () => setClient(makeClient()),
      }}
    >
      <UrqlProvider value={client}>{children}</UrqlProvider>
    </ClientContext.Provider>
  );
}

const useClient = () => React.useContext(ClientContext);

The context itself exposes a resetClient function that simple creates a new client and updates its state with it. The URQL provider is passed it as a prop.

Now, elsewhere in my app, anytime I want to flush the cache I can just get the resetStore function from useClient and call it. URQL then refetches any queries that're needed:

const { resetClient } = useClient();

All 9 comments

So, we don't support this currently, but you have two options I believe.

You could write your own cache exchange based on ours. This would probably be a simple copy and paste with an added method. For instance on the SSR exchange we have some special methods to extract or restore data. So you could add a method to reset the cache.

The other option would be to wrap around the urql provider and completely recreate the client when the user logs out.

I can maybe write up some code samples if you need some more concrete help on that 馃憤

Thanks for the prompt reply 馃檶. I think the 2nd option feels a bit cleaner. I'll give it a go.

Some code samples would be great. I imagine that this is quite a common scenario for a lot of apps with authentication.

So if anyone else is interested in how I did this here's an example:

Define a makeClient function. Mine looks like this.

const makeClient = () =>
  createClient({
    url: process.env.REACT_APP_API_URL || '',
    fetchOptions: () => {
      const token = store.get('auth_token');
      if (!token) {
        return {};
      }
      return {
        headers: {
          authorization: `Bearer ${token}`,
        },
      };
    },
  });

Create a context that should "house" the client. One of its props should be the makeClient function. You could just use the createClient function directly imported from URQL but this approach allows dependency injection so different clients can be used depending on the situation, as well as any app-specific configuration can be placed outside of this component.

const ClientContext = React.createContext<ClientState>({
  // this is just to satisfy the TS compiler. If you're using JS you can just omit the default value.
  resetClient: always(undefined),
});

function ClientProvider({ makeClient, children }: Props) {
  const [client, setClient] = React.useState(makeClient());

  return (
    <ClientContext.Provider
      value={{
        resetClient: () => setClient(makeClient()),
      }}
    >
      <UrqlProvider value={client}>{children}</UrqlProvider>
    </ClientContext.Provider>
  );
}

const useClient = () => React.useContext(ClientContext);

The context itself exposes a resetClient function that simple creates a new client and updates its state with it. The URQL provider is passed it as a prop.

Now, elsewhere in my app, anytime I want to flush the cache I can just get the resetStore function from useClient and call it. URQL then refetches any queries that're needed:

const { resetClient } = useClient();

Glad you resolved this! I'll close this issue for now, since it's technically resolved sufficiently? But maybe this would be a great addition to our docs 馃憤 could be just a small FAQ entry explaining that resetting the client by recreating it is the easiest way to clear all of its state

@kitten Is this still the recommended way of completely clearing/invalidating Urql's cache? My use case is on logout. I need the cache to be completely cleared on logout. I'm fine with the introspection query being resent if necessary, which the above solution will obviously do. Thank you for your time!...and btw, Urql rocks!!

@Natas0007 Yep, that's still the safest approach to make sure everything's cleared :+1: also the introspection query is only sent in development when @urql/devtools is used.

@kitten This seems to be working as expected, but it leads me to a quick follow up question. Since the devtools (Chrome) don't seem to update after recreating the client via resetClient()...and right click -> "Reload frame" on the devtools seems to completely clear the devtools until new queries are executed, what is the best way to verify the cache has been cleared? Do I need to write graphcache resolvers to get to the cache prop? Not a problem if so, and this will be a one-off test...I'm just curious as to the proper way of doing it. Thanks again for your time!

@Natas0007 The cache will be cleared since it'll be recreated along with the client. That being said, creating a new client should definitely reset all data in the dev tools as well, so that may be worth an issue report over on the devtools repo since that sounds like a regression 馃槄

@kitten Thanks again!

Issue created: https://github.com/FormidableLabs/urql-devtools/issues/325

Was this page helpful?
0 / 5 - 0 ratings

Related issues

frederikhors picture frederikhors  路  3Comments

dotansimha picture dotansimha  路  4Comments

ivosequeros picture ivosequeros  路  5Comments

maxzhuk0v picture maxzhuk0v  路  3Comments

narinluangrath picture narinluangrath  路  3Comments