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

Have a way to clear cache #161

Closed
sergiodxa opened this issue Nov 28, 2019 · 28 comments · Fixed by #231
Closed

Have a way to clear cache #161

sergiodxa opened this issue Nov 28, 2019 · 28 comments · Fixed by #231
Labels
area: cache feature request New feature or request

Comments

@sergiodxa
Copy link
Contributor

I'm writing tests for a component using useSWR, also with { suspense: true }, and if I render a component reading the same key (e.g. the same component with different keys) I'm getting the old cache in my second test.

Not sure if this will be addressed with #158, but having a way to tell SWR to clear the cache of a certain key would be awesome for this use case.

import { clearCache } from "swr";

clearCache(); // clear all cache
clearCache("/api/data"); // clear only a certain cache key

☝️ something like that.

What I have tried:

  • mutate(key, null, false)
  • mutate(key, {}, false)

Both doesn't work right since useSWR will still read that null or {} from the cache instead of triggering an immediate fetch and suspending the component.

I will try to create a CodeSandbox to replicate this soon.

@sergiodxa
Copy link
Contributor Author

For testing purposes what I made to solve this is to ensure I'm always using different keys, since they are based on a prop, but still would be nice the be able to do:

import { clearCache } from "swr";
afterEach(() => clearCache);

And ensure all my tests have zero cached data.

@shuding shuding added the feature request New feature or request label Nov 29, 2019
@shuding
Copy link
Member

shuding commented Nov 29, 2019

What do you think about the idea of exporting the cache object directly?

@shuding
Copy link
Member

shuding commented Nov 29, 2019

Another thing I've been thinking is #92, which will only remove the inactive keys.
If we clear/change the cache of an active SWR hook, it might cause some unexpected behavior.

@sergiodxa
Copy link
Contributor Author

So far I haven't needed, until now, direct access to the internal cache, with it I could have done what I needed creating the clearCache function myself.

I agree that cleaning the cache it's not something you should want in production, I only need it for testing purposes.

Does SWR listen to cache changes? Maybe this way if an active SWR hook is using a cleared key it could automatically trigger a new fetch as if it was new? 🤔 This way if you still have a rendered component using the cleared key it wouldn't break, just change to loading state as if it was a new key? Just thinking out loud.

@gabrieledarrigo
Copy link

gabrieledarrigo commented Jan 13, 2020

Agree to expose a way to clear the cache, it's a common scenario.
For example, with a simple component that fetches data:

it('should fetch on mount', async () => {
  await act(async () => {
    <Foo id="1" />
  });

  expect(fetch).toHaveBeenCalledWith('1');
});

it('should show an error when fetch fails', async () => {
  (fetch as jest.Mock).mockRejectedValue(new Error('An error occured'));

  await act(async () => {
    mount(
      <Foo id="1" />
    );
  });

  expect(fetch).toHaveBeenCalledWith('1');
  // other assertions that the error message is rendered
});

The second expectation fails, 'cause SWR dedupes the requests, returning the first result from the cache.
The only way to test this behavior is to change the id propr, but it's not always feasible, depending on the test scenario.

@andreoav
Copy link

I'm having problems with SWR and jest tests. It fails when a test has a swr call that was used in a previous test.

  1. Run a test that returns a list using SWR -> TEST PASS
  2. Run a test (with the same key), but change the return to test a different scenario -> TEST FAILS (returns the cached result from the previous test).

@sergiodxa sergiodxa mentioned this issue Jan 30, 2020
JulianG added a commit to JulianG/swr that referenced this issue Feb 17, 2020
Fixes vercel#161 by simply exposing an existing function.
I added my tests to the end of the use-swr.test.tsx file because the file wasn't long enough.
@JulianG
Copy link

JulianG commented Feb 24, 2020

Awesome!

@andreoav
Copy link

andreoav commented Feb 26, 2020

@quietshu something is weird here. When testing, my second test stills renders with the result provided by a previous test.

I tried a cache.clear() in a beforeEach and before rendering the component within the test, both fails.

If I run each test at a time they pass.

@sergiodxa
Copy link
Contributor Author

@andreoav check my answer here #231 (comment)

@ConsoleTVs
Copy link

@sergiodxa

I am using a similar idea on my SWR vue library. In fact, i find this statement wrong:

I agree that cleaning the cache it's not something you should want in production, I only need it for testing purposes.

I've written some applications where the cache TTL is about 1 minute. So, if I visit the dashboard and it fetches some data with a TTL 1 min, then logout and login with another user (with less than a minute apart) then that cache still shows the old user values and does not revalidate yet due TTL < 1 min.

The quickest solution is to delete all cached keys or the ones specified at logout, this ensures fresh data upon login.

This is the clear function I did: https://github.com/ConsoleTVs/vswr/blob/6a84e923d9db31784316b975f98db1f5eaea3140/src/swr.ts#L219

@sergiodxa
Copy link
Contributor Author

@ConsoleTVs what i usually do is to reload the page in a logout so cache is cleared, also most users don't login, see data, logout and see new data in less than 1 minutes IMO.

However, with the current cache you can do it with cache.clear() if you don't reload on logout.

@ConsoleTVs
Copy link

Just keep in mind, that the (mobile) application we're building probably have a safe cache of Infinity, since we know exactly when we need to reload / mutate it via events, 1 min was just for testing :)

Nevertheless, is it possible with cache.clear() on react's SWR? How do you access the cache instance then?

@sergiodxa
Copy link
Contributor Author

You can import the cache and clear it with:

import { cache } from "swr"
cache.clear()

This has been available since #231 was merged

@ConsoleTVs
Copy link

Is this actually documented on the docs page? Very interesting indeed

@sergiodxa
Copy link
Contributor Author

It's not documented yet

@AlaaZorkane
Copy link

Ha! Been looking for this, can someone please document this, very important feature.

@plaa
Copy link

plaa commented Apr 12, 2021

what i usually do is to reload the page in a logout so cache is cleared

@sergiodxa Page reload is not possible in a React Native app. 😀

I was surprised the documentation didn't cover this. I would have expected it to be at the bottom of the Mutation page which deals with cache updates anyway.

This also simplifies handling authentication, as you can just clear the cache every time authentication changes. In this case the fetcher method can obtain access tokens itself instead of passing them in the key every time (see #325 (comment)).

@andersnylund
Copy link

andersnylund commented Sep 3, 2021

This seems to have been updated at some point, but couldn't find the exact commit or PR when.

I was able to create a new cache on each test with this:

import { SWRConfig } 'swr';

<SWRConfig value={{ provider: () => new Map() }}>
  {/* SWRConfig is given a new provider on each test so that it's caching is emptied between each test */}
  {children} {/* or the component you want to test that uses `useSWR` */}
</SWRConfig>

https://swr.vercel.app/docs/advanced/cache#reset-cache-between-test-cases

@maapteh
Copy link

maapteh commented Oct 26, 2021

But then you have to wrap new config per test, instead of just using one. I also used the cache.clear() undocumented feature and found out its gone now :( It was super helpful for tests

@tobslob
Copy link

tobslob commented Feb 4, 2022

You can import the cache and clear it with:

import { cache } from "swr"
cache.clear()

This has been available since #231 was merged

What version of swr is this?

@alexlomm-dormzi
Copy link

Doesn't seem like the cache is being exported any more as of v1.1.2. However @andersnylund's solution of providing the wrapper works great.

Also, for those of you who want use <SWRConfig /> with renderHook(...) the way to to that would be:

const wrapper = ({ children }) => <SWRConfig value={{ provider: () => new Map() }}>{children}</SWRConfig>

test('test something', () => {
  // provide a "clean" wrapper for `swr`
  const { result } = renderHook(useFetchWithSWR, { wrapper })

  // etc...
})

@JamieKudla
Copy link

@andersnylund method of using a wrapper with a cache provider is the way to do this as of 1.0.0 per the docs: https://swr.vercel.app/docs/advanced/cache#reset-cache-between-test-cases

@ConsoleTVs
Copy link

ConsoleTVs commented Feb 22, 2022

@JamieKudla they literally list a clear method at the end of thst page, is that not working?

@Ajido
Copy link

Ajido commented Mar 17, 2022

There is no function defined to delete all caches. Is the documentation wrong?
https://swr.vercel.app/docs/advanced/cache#reset-cache-between-test-cases

image
image

@soullivaneuh
Copy link

soullivaneuh commented Mar 17, 2022

@andersnylund comment (#161 (comment)) looks to do the trick, same solution for Storybook using a decorator:

import React from 'react';
import { 
  DecoratorFn,
} from '@storybook/react';
import {
  SWRConfig,
} from 'swr'

export const StoryDecorator: DecoratorFn = (Story: any, {}) => {
  return (
    <SWRConfig value={{ provider: () => new Map() }}>
      <Story />
    </SWRConfig>
  );
};

export default null;

@clement-escolano
Copy link

clement-escolano commented Mar 25, 2022

@Ajido
Type definition of cache is not up to date but the code actually works. If you want to fix the type issue, you can override the Cache type by adding this:

import "swr/dist/types";

declare module "swr/dist/types" {
    export interface Cache {
        clear(): void;
    }
}

Vinnl added a commit to Vinnl/swr that referenced this issue Apr 20, 2022
The Cache has been exposed since [1], which supposedly includes a
.clear() method [2], which in turn is not actually present in the
type definition [3]. This adds it to the type definition to align
with the implementation.

Since Map also contains a .clear() method [4], this shouldn't break
the ability to pass that in as a map.

[1] vercel#231
[2] vercel#161 (comment)
[3] vercel#161 (comment)
[4] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map/clear
@bozdoz
Copy link

bozdoz commented Jul 27, 2022

This is super awkward to implement. Would love a clear way to clear cache for all tests.

@huozhi
Copy link
Member

huozhi commented Jul 27, 2022

Please checkout #1887 (comment) for the new cache clear approach introduced in v2 beta

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area: cache feature request New feature or request
Projects
None yet
Development

Successfully merging a pull request may close this issue.