Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ jobs:
fetch-depth: 0

- name: Cache node modules
uses: actions/cache@v2
uses: actions/cache@v4
id: cache-npm-build
env:
cache-name: cache-node-modules
Expand All @@ -31,7 +31,7 @@ jobs:

- name: npm install
if: steps.cache-npm-build.outputs.cache-hit != 'true'
run: npm ci
run: npm i

- name: lint
run: npm run lint
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
with:
node-version: 20.11.0
- name: Install dependencies
run: npm ci
run: npm i
- name: build
run: npm run build
- name: test
Expand Down
49 changes: 46 additions & 3 deletions docs/caching.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,15 +114,15 @@ export const useGetUser = (id: string, role: Role) => {

_WHAT'S THE FINAL CACHE KEY VALUE?: Assuming the `id` parameter in the above example is `1`, and the `role` parameter is `administrator`, the final cache key value for this query will be `user.getUser.administrator-1`. Please see the above "The anatomy of a cache key value" section to understand what this means._

## The mutate function - Validating the State
## The invalidate function - Validating the State

When performing a mutation which results in a data change, it's important to invalidate any associated state from a [useQuery](use-query.md) or [useInfiniteQuery](use-infinite-query.md).

Take for example the below user update mutation hook. We _know_ that a successful update to the user object will immediately make both the user list data and individual user data stale, so we need to invalidate the cache like so:

```TypeScript
export const useUpdateUser = () => {
const { mutate: invalidate } = useSWRConfig();
const { invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.updateUser.useMutation();

const updateUser = React.useCallback(async (user: IUser) => {
Expand All @@ -147,6 +147,32 @@ Invalidating cache does one of two things depending on whether the newly invalid
1. **Data is currently being rendered at invalidation time**: This will cause an immediate refetch from the server on the query in question.
2. **Data is not being rendered at invalidation time**: This will invalidate the cached data behind the scenes, and force a refetch from the server the next time the data is used.

### Invalidating an infinite query

SWR doesn't handle global invalidations of infinite queries very well. Luckily, API-SWR has you covered by providing a special invalidation hook designed for infinite queries. This hook only requires the `cacheKey`, ne need to worry about `startsWithInvalidator` here:

```TypeScript
export const useUpdateUser = () => {
const { invalidateInfinite, invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.updateUser.useMutation();

const updateUser = React.useCallback(async (user: IUser) => {
// run the user update
const response = await clientFetch({ user });
// if it was successful
if (response.data) {
// invalidate all cache associated with the `getUsers` infinite list query
invalidateInfinite(userApi.getUsers.cacheKey());
// invalidate all cache associated with the specific user that's been updated (based on their cache key)
invalidate(userApi.getUser.cacheKey(response.data.id));
}
return response;
}, [clientFetch]);

return { updateUser, ...rest };
};
```

### Knowing what to invalidate

API SWR provides two helper functions for accessing the cache key associated with a specific endpoint. If you haven't yet read "The anatomy of a cache key value" section above, please do that first in order to understand what a cache key value is.
Expand Down Expand Up @@ -179,7 +205,7 @@ invalidate(userApi.getUser.cacheKey(response.data.id))

This will invalidate the cache for a specific user within a specific endpoint.

#### For all data that starts with a cache key value
#### For all data that starts with a cache key value (not relevant for infinite queries)

The `startsWithInvalidator` function available on on each endpoint works in exactly the same way as the `cacheKey` function, but it allows you to invalidate all cached data with a cache key that **starts with** the supplied cache key. This is particularly useful for paged endpoints for which the page number is supplied as a cacheKey parameter, but you want to invalidate _all_ pages.

Expand All @@ -191,6 +217,23 @@ invalidate(userApi.getUsers.startsWithInvalidator())

This will invalidate all pages of our paged `user.getUsers` endpoint.

## Clearing all cache

The `useCacheManager` hook provides a method for clearing all cache associated with your app (usually called on logout or similar context reset scenarios). Here's an example:

```TypeScript
const { clearAll } = useCacheManager();

const logout = React.useCallback(() => {
clearAll();
}, []);
```

## Other tools returned by `useCacheManager`

- `mutateInfinite` - similar to `invalidateInfinite` but also supports the other arguments on the [SWR global mutate](https://swr.vercel.app/docs/mutation#global-mutate) for more advanced cache management.
- The `useCacheManager` hook also returns everything returned by the SWR `useSWRConfig` for full control over your cache.

## SWR Documentation

For further information on how SWR handles caching under the hood, take a look at their documentation [here](https://swr.vercel.app/docs/advanced/cache).
9 changes: 4 additions & 5 deletions docs/generic-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
The below example shows a `userApi` controller alongside a set of example CRUD endpoint hooks.

```TypeScript
import { pagingConfig } from "@rocketmakers/api-swr"
import { pagingConfig, useCacheManager } from "@rocketmakers/api-swr"
import { apiFactory } from "../controllerFactory.ts"
import { userApi } from "example-api-client";
import { useSWRConfig } from 'swr';

/** CONTROLLER **/

Expand All @@ -37,7 +36,7 @@ export const useGetUser = (userId: string) => {
};

export const useCreateUser = () => {
const { mutate: invalidate } = useSWRConfig();
const { invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.createUser.useMutation();

const createUser = React.useCallback(async (user: ICreateUser) => {
Expand All @@ -52,7 +51,7 @@ export const useCreateUser = () => {
};

export const useUpdateUser = () => {
const { mutate: invalidate } = useSWRConfig();
const { invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.updateUser.useMutation();

const updateUser = React.useCallback(async (user: IUser) => {
Expand All @@ -68,7 +67,7 @@ export const useUpdateUser = () => {
};

export const useDeleteUser = () => {
const { mutate: invalidate } = useSWRConfig();
const { invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.deleteUser.useMutation();

const deleteUser = React.useCallback(async (id: string) => {
Expand Down
9 changes: 4 additions & 5 deletions docs/openapi-controller.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,9 @@
The below example shows a `userApi` controller alongside a set of example CRUD endpoint hooks.

```TypeScript
import { pagingConfig } from "@rocketmakers/api-swr"
import { pagingConfig, useCacheManager } from "@rocketmakers/api-swr"
import { apiFactory } from "../controllerFactory.ts"
import { UserApi } from "example-api-client";
import { useSWRConfig } from 'swr';

/** CONTROLLER **/

Expand All @@ -37,7 +36,7 @@ export const useGetUser = (userId: string) => {
};

export const useCreateUser = () => {
const { mutate: invalidate } = useSWRConfig();
const { invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.createUser.useMutation();

const createUser = React.useCallback(async (user: ICreateUser) => {
Expand All @@ -52,7 +51,7 @@ export const useCreateUser = () => {
};

export const useUpdateUser = () => {
const { mutate: invalidate } = useSWRConfig();
const { invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.updateUser.useMutation();

const updateUser = React.useCallback(async (user: IUser) => {
Expand All @@ -68,7 +67,7 @@ export const useUpdateUser = () => {
};

export const useDeleteUser = () => {
const { mutate: invalidate } = useSWRConfig();
const { invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.deleteUser.useMutation();

const deleteUser = React.useCallback(async (id: string) => {
Expand Down
2 changes: 1 addition & 1 deletion docs/use-mutation.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ Like all API SWR endpoint hooks, the `useMutation` hook _can_ be consumed direct

```TypeScript
export const useUpdateUser = () => {
const { mutate: invalidate } = useSWRConfig();
const { invalidate } = useCacheManager();
const { clientFetch, ...rest } = userApi.updateUser.useMutation();

const updateUser = React.useCallback(async (user: IUser) => {
Expand Down
Loading