-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
Expose Cache #231
Expose Cache #231
Conversation
@quietshu I updated this PR to only expose the cache without letting the user replace it, let me know if you want me to change something, I will try again to allow to replace the cache once I think a good way to let mutate identify the correct cache. |
Thank you @sergiodxa so much for working on this! I read the code briefly and it looks great and very clean 👍 And yeah indeed it will be a tricky problem for global mutation, as well as namespaced cache (if we're gonna do that). I will review the code again carefully and think about that too. |
Any news on this? |
Also curious, looking to hopefully have it fix #237 |
This should also resolve #158 |
This PR solves some problems we have with SWR and testing. |
Having a way to clear cache is also useful for when one component does an API request and you need another component to update what it's displaying and not show its previous data (without having to use ugly callbacks and refs). |
Ciao guys, any update on this? |
@gabrieledarrigo I’m waiting for Shu to review it |
Hi, great job on this PR |
@nulladdict you are right, I move them to the Cache class as a single |
@quietshu you are right, it will make more sense, and it will still work if the user pass the serialized string directly, and applied it in b4ed289 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great. Let's ship it!
Released |
Makes sense, I will try it in production too |
I can also test this with the use case I described above. If I don't reply, assume everything worked fine. 🙂 |
I tested the feature with my application and it has solved the problem I was having before. Thanks! |
I've also tested this in prod, and it works pretty fine for us. const [_key, , _errorKey] = cache.serializeKey('key')
const errorKeyPrefix = _errorKey.replace(_key, '') So I think it might've also been useful to expose the hardcoded |
Ciao guys, big up for your work on this! import { cache } from 'swr';
afterEach(() => {
cache.clear()
});
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
}); But the second test doesn't pass because |
@gabrieledarrigo I have the same problem, even with @sergiodxa @quietshu Do we need to do something special to clear the cache during testing? |
@andreoav @gabrieledarrigo I tried it in a test and found the issue, is because the deduping feature of SWR, since not enough time has passed since the last request SWR is reusing the fetch, you can fix it in two ways:
I recommend to use the second option so your component will still have deduping of requests enabled while running inside your app and you can disable it only for the test. I will send a PR with an example soon. Using the example code above something like this should work: import { SWRConfig, cache } from 'swr';
afterEach(() => {
cache.clear()
});
it('should fetch on mount', async () => {
await act(async () => {
<SWRConfig value={{ dedupingInterval: 0 }}>
<Foo id="1" />
</SWRConfig>
});
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(
<SWRConfig value={{ dedupingInterval: 0 }}>
<Foo id="1" />
</SWRConfig>
);
});
expect(fetch).toHaveBeenCalledWith('1');
// other assertions that the error message is rendered
}); |
@sergiodxa Nice!! I updated my tests and it worked. I think we should add this to the documentation. |
Thanks a lot for your work on this, this is really helpful! As far as I can tell, this hasn't been released to npm yet, right? Any chance to get a release? Thanks! |
@amannn It was released as a beta: #231 (comment) |
Ah right, thanks! |
Thanks so much for share this. I think would be nice to have this kind of info on docs. |
So is there a way I can access the cache variable? I want to fetch only once and I want a condition like !cache.has(dataKey) or similar |
@p19ky I use this way of calling swr to only have it fetch once. return useSWR<string>(API_PICTURES_IMAGE(pictureId), {
// Images cannot be changed so should never be revalidated
revalidateOnFocus: false,
revalidateOnMount: false,
revalidateOnReconnect: false,
}); |
When I tried it this way, it does not load once. I does not load at all. So what I did to resolve this is the following:
|
I am running into this again and again. Why isn't Solutions:
|
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
This attempts to partially resolve #212 and resolves #161 and resolves #237 and resolves #158
Implementation Notes
I updated this to only expose the global cache, I kept the cache interface, adding a way to tell the
set
,delete
andclear
methods if you want to notify components reading from the cache, this waymutate
can callcache.set
andcache.set
can also callmutate
as a notification method without running on a infinite loop (mutate callscache.set
with this flag asfalse
).I did this to come back to custom cache once I found a way to let
mutate
know which cache to call without manually passing it.I created a CacheInterface and make the global config create a default instance of the cache, also exported both the interface and the Cache class.I also updateduseSWR
to read from the cache instance received through the context object.I was not able, yet, to find a way to makemutate
andtrigger
read from the correct cache instance, because they are global my only idea so far was to pass the cache instance as a third argument (probably optional), but that could make it hard to test components using a custom cache for tests and a default cache for the mutate call.Missing things
-useSWRPages
is still using global cache (I haven't tried yet to use it here but I believe is the same asmutate
andtrigger
)-mutate
is still using global cache-trigger
is still using global cacheAny feedback is welcomed!