Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cache.clear() not available even though docs imply that it is #1887

Closed
mfreeman-xtivia opened this issue Mar 11, 2022 · 6 comments
Closed

cache.clear() not available even though docs imply that it is #1887

mfreeman-xtivia opened this issue Mar 11, 2022 · 6 comments

Comments

@mfreeman-xtivia
Copy link

We still need a function to clear the entire cache contents

I have seen several bugs closed here recently with a reference to the fix introduced in 1.2.2 where you can now call cache.clear(key)

HOWEVER this still does match the documentation, which asserts that there is a cache.clear() function that can be used to clear the entire contents of the cache. Typescript complains that there is no such function.

Bottom line: is there any way to clear the entire contents of the cache?

@majelbstoat
Copy link

If you're using the default cache, which is a JS map, there is a clear method, it's not just on SWR's Cache type.

But you can workaround this with:

const {cache} = useSWRConfig()
const c = cache as any
c.clear()

Not pretty, but it will work until the type is fixed.

@majelbstoat
Copy link

majelbstoat commented Apr 1, 2022

Though, I would actually prefer cache.keys() to be exposed, because clearing the cache without a mutate might conceivably leave swr in a place where there's no data and swr doesn't know to refetch it, so i think:

    const c = cache as any
    for (const key of c.keys() {
      mutate(key)
    }

might be slightly better.

@michaeldrotar
Copy link

For some reason I couldn't get cache.clear to work for me - subsequent tests still kept having the mocked response of the first test.

I even went so far as to wrap things in async code and act(...) to be super safe

afterEach(async () => {
  const hook = renderHook(() => useSWRConfig())
  await act(() => {
    const cache = hook.result.current.cache as Map<string, any>
    await cache.clear()
  })
})

From the code, it definitely looks like useSWR(...) grabs const cached = cache(key) and if it's not there then this should work.

Iterating cache.keys() and passing them to mutate(key, undefined) does not work because the keys stored there are serialized and mutate(...) needs the unserialized version.

This has been a huge frustration for the past few weeks and I've tried many different hoops to write reasonable tests.

Currently I've landed on a solution that wraps useSWR(...) so that I can store every key used in a Set<string> and provide my own clear() method. The exact implementation isn't shareable but it's roughly like this:

// global storage
const cache: Set<string> = new Set()

export function useSWRPlus(key, ...) {
  const swr = useSWR(() => {
    // need to always use the fn method of key to do the processing, so something like
    const normalizedKey = typeof key === 'function' ? key() : key
    if (normalizedKey) {
      cache.add(JSON.stringify(normalizedKey))
      return normalizedKey
    },
    ...
  )
  // Unfortunately it always needs this now even though it'll almost never be used
  const swrConfig = useSWRConfig()
  return {
    ...swr,
    async clear() {
      const keys = cache.keys()
      for (const key of keys) {
        const parsedKey = JSON.parse(key)
        // You could add some conditional here to only match certain keys, maybe that's part
        // of your swr wrapper or maybe your clear method can optionally take some matcher
        await swrConfig.mutate(parsedKey, undefined)
        cache.delete(key)
      }
    }
  }
}

Probably better would be to move the global cache to another file and add a useSWRPlusConfig that adds this method - then both can share the global cache without this needing to useSWRConfig every call 🤔

@huozhi
Copy link
Member

huozhi commented Jun 30, 2022

We released a new API change latest beta v2.0.0-beta.5 that could let you do the following code to remove all cache data:

mutate(/* match all keys */() => true, undefined, false)

Notice that the functional key is replaced by a key filter function. That's the better way than cache.clear because it could keep the state sync with the actual cache provider. We don't recommend to manipulate the cache directly in the codebase.

@ConsoleTVs
Copy link

ConsoleTVs commented Jul 27, 2022

This API seems quite awkward.

Try to give this code to somebody who haven't read the docs and tell them to guess what it does and what each param does.

mutate(() => true, undefined, false)

What's preventing us from having a forget(keys) and clear() ?

@huozhi
Copy link
Member

huozhi commented Jul 27, 2022

The v2 docs are still in progress, the more details about the mutate API change are in PR #1989 or RFC #1946

The new mutate API will look like below which can take either a function keyFilter or any key, data and revalidate arguments are still as same as before

mutate(keyFilter | key, data, revalidate?)

The idea was to leverage the existing mutate API but extend the ability to partially mutate few selected keys. That example calls means: Use () => true as a filter is to matching all the existing keys and reset to undefined, without revalidation.

We'll give more examples about that change in v2 docs later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants