Skip to content

Commit

Permalink
Improve documentation for cache functions (#654)
Browse files Browse the repository at this point in the history
Co-authored-by: Sarah <[email protected]>
Co-authored-by: Sarah Gerrard <[email protected]>
  • Loading branch information
3 people committed May 26, 2024
1 parent 6865828 commit 83193de
Showing 1 changed file with 71 additions and 29 deletions.
100 changes: 71 additions & 29 deletions src/routes/solid-router/reference/data-apis/cache.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,29 +2,35 @@
title: cache
---

Cache is used to prevent duplicate fetching and to trigger handle refetching.
It takes a function and returns the same function.
`cache` is a [higher-order function](https://en.wikipedia.org/wiki/Higher-order_function) designed to create a new function with the same signature as the function passed to it.
When this newly created function is called for the first time with a specific set of arguments, the original function is run, and its return value is stored in a cache and returned to the caller of the created function.
The next time the created function is called with the same arguments (as long as the cache is still valid), it will return the cached value instead of re-executing the original function.

```jsx
const getUser = cache((id) => {
return (await fetch(`/api/users${id}`)).json()
}, "users") // used as cache key + serialized arguments
```

<Callout>
`cache` can be defined anywhere and then used inside your components with [`createAsync`](/solid-router/reference/data-apis/create-async).

It is expected that the arguments to the cache function are serializable.
However, using `cache` directly in [`createResource`](/reference/basic-reactivity/create-resource) will not work since the fetcher is not reactive and will not invalidate properly.

This cache accomplishes the following:
</Callout>

1. Deduping on the server for the lifetime of the request.
2. Preloading the cache in the browser - this lasts 10 seconds.
When a route is preloaded on hover or when load is called when entering a route it will make sure to dedupe calls.
3. A reactive refetch mechanism based on key.
This prevents routes that are not new from retriggering on action revalidation.
4. Serve as a back/forward cache for browser navigation for up to 5 minutes.
Any user based navigation or link click bypasses it.
Upon revalidation or new fetch the cache is updated.
## Usage

```js
const getUser = cache(
(id, options = {}) => fetch(`/api/users/${id}?summary=${options.summary || false}`).then(r => r.json()),
"usersById"
);

getUser(123); // Causes a GET request to /api/users/123?summary=false
getUser(123); // Does not cause a GET request
getUser(123, { summary: true }); // Causes a GET request to /api/users/123?summary=true
setTimeout(() => getUser(123, { summary: true }), 999000); // Eventually causes another GET request to /api/users/123?summary=true
```

### With load functions

Using it with a load function:
Using it with a [load function](https://docs.solidjs.com/solid-router/reference/load-functions/load):

```js
import { lazy } from "solid-js";
Expand All @@ -42,7 +48,9 @@ function loadUser({params, location}) {
<Route path="/users/:id" component={User} load={loadUser} />;
```

Inside your page component you:
### Inside a route's component

Using it inside a route's component:

```jsx
// pages/users/[id].js
Expand All @@ -54,22 +62,56 @@ export default function User(props) {
}
```

Cached functions provide a few useful methods for getting the key that can be used in cases with invalidation:
## Cach function capabilities

`cache` accomplishes the following:

1. Deduping on the server for the lifetime of the request.
2. Preloading the cache in the browser - this lasts 10 seconds.
When a route is preloaded on hover or when load is called when entering a route it will make sure to dedupe calls.
3. A reactive refetch mechanism based on key.
This prevents routes that are not new from retriggering on action revalidation.
4. Serve as a back/forward cache for browser navigation for up to 5 minutes.
Any user based navigation or link click bypasses it.
Upon revalidation or new fetch the cache is updated.

## Cache keys

To ensure that the cache keys are consistent and unique, arguments are deterministically serialized using JSON.stringify.
Before serialization, key/value pairs in objects are sorted so that the order of properties does not affect the serialization.
For instance, both `{ name: 'Ryan', awesome: true }` and `{ awesome: true, name: 'Ryan' }` will serialize to the same string so that they produce the same cache key.

## Return value

The return value is a `CachedFunction`, a function that has the same signature as the function you passed to `cache`.
This cached function stores the return value using the cache key.
Under most circumstances, this temporarily prevents the passed function from running with the same arguments, even if the created function is called repeatedly.

## Arguments

| argument | type | description |
| -------- | ---- | ----------- |
| `fn` | `(...args: any) => any` | A function whose return value you'd like to be cached. |
| `name`* | string | Any arbitrary string that you'd like to use as the rest of the cache key. |

*Since the internal cache is shared by all the functions using `cache`, the string should be unique for each function passed to `cache`.
If the same key is used with multiple functions, one function might return the cached result of the other.

## Methods

### `.key` and `.keyFor`

Cached functions provide `.key` and `.keyFor`, are useful when retrieving the keys used in cases involving invalidation:

```ts
let id = 5;

getUser.key; // returns "users"
getUser.keyFor(id); // returns "users[5]"
```

### `revalidate`

The cache can be revalidated using the `revalidate` method or the `revalidate` keys that are set on the response from the actions.
If the whole key is passed, it will invalidate all entries for the cache (ie. `users` in the example above).
If the entire key is passed, it will invalidate all entries for the cache (ie. `users` in the example above).
If only a single entry needs to be invalidated, `keyFor` is provided.

<Callout>
`cache` can be defined anywhere and then used inside your components with [`createAsync`](/solid-router/reference/data-apis/create-async).

However, using `cache` directly in [`createResource`](/reference/basic-reactivity/create-resource) will not work since the fetcher is not reactive and will not invalidate properly.

</Callout>
To revalidate everything in the cache, pass `undefined` as the key.

0 comments on commit 83193de

Please sign in to comment.