remix-client-cache
remix-client-cache 是一个强大且轻量级的库,专为 Remix.run 打造,用于使用 clientLoaders 在客户端缓存服务器加载器数据。
默认情况下,它使用 stale while revalidate 策略,并在从服务器加载后热交换您的过时信息。它还允许您使特定键或多个键的缓存失效。
它允许您传入您选择的适配器来缓存您的数据。
它带有一个默认适配器,使用内存存储来缓存您的数据。
对 localStorage、sessionStorage 和 localforage 包的一级支持。您只需将它们作为参数提供给 configureGlobalCache
。
安装
npm install remix-client-cache
基本用法
这是一个使用带有默认内存适配器的 remix-client-cache 的示例用法。
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { ClientLoaderFunctionArgs } from "@remix-run/react";
import { cacheClientLoader, useCachedLoaderData } from "remix-client-cache";
export const loader = async ({ params }: LoaderFunctionArgs) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${params.user}`
);
const user = await response.json();
await new Promise((resolve) => setTimeout(resolve, 1000));
return json({ user: { ...user, description: Math.random() } });
};
// Caches the loader data on the client
export const clientLoader = (args: ClientLoaderFunctionArgs) => cacheClientLoader(args);
// make sure you turn this flag on
clientLoader.hydrate = true;
export default function Index() {
// The data is automatically cached for you and hot swapped when refetched
const { user } = useCachedLoaderData<typeof loader>();
return (
<div>
{user.name} <hr /> {user.email}
<hr />
{user.username}
<hr />
{user.website} <hr />
{user.description}
</div>
);
}
缓存适配器
该库导出一个接口,您需要实现该接口才能创建自己的缓存适配器。该接口称为 CacheAdapter
。它与 Storage
接口非常匹配,并要求您具有以下方法
getItem
:获取一个键并返回一个 promise,该 promise 解析为存储在该键处的值setItem
:获取一个键和一个值并返回一个 promise,该 promise 在存储值时解析removeItem
:获取一个键并返回一个 promise,该 promise 在删除值时解析
cacheLoaderData
将使用库附带的默认内存缓存适配器。如果您想要一个高级用例,请确保您提供的适配器实现了 CacheAdapter
接口。
// Inside your entry.client.tsx file
import { RemixBrowser } from "@remix-run/react";
import { startTransition, StrictMode } from "react";
import { hydrateRoot } from "react-dom/client";
import { configureGlobalCache } from "remix-client-cache";
// You can use the configureGlobalCache function to override the libraries default in-memory cache adapter
configureGlobalCache(() => localStorage); // uses localStorage as the cache adapter
startTransition(() => {
hydrateRoot(
document,
<StrictMode>
<RemixBrowser />
</StrictMode>
);
});
您可以使用 configureGlobalCache
函数覆盖库的默认内存缓存适配器。它将全局切换到您提供给它的任何适配器。
如果您希望拥有每个路由的适配器,您可以使用 createCacheAdapter
创建适配器并将其提供给您的钩子和函数。
import { createCacheAdapter, useCachedLoaderData } from "remix-client-cache";
const { adapter } = createCacheAdapter(() => localStorage); // uses localStorage as the cache adapter
// Caches the loader data on the client
export const clientLoader = (args: ClientLoaderFunctionArgs) => cacheClientLoader(args, {
// We pass our custom adapter to the clientLoader
adapter
});
// make sure you turn this flag on
clientLoader.hydrate = true;
export default function Index() {
const { user } = useCachedLoaderData<typeof loader>({
// We use the adapter returned by the createCacheAdapter function
adapter
});
return (
<div>
{user.name} <hr /> {user.email}
<hr />
{user.username}
<hr />
{user.website} <hr />
{user.description}
</div>
);
}
以下是如何使用不同全局适配器的库的一些示例。
configureGlobalCache(() => localStorage); // uses localStorage as the cache adapter
configureGlobalCache(() => sessionStorage); // uses sessionStorage as the cache adapter
configureGlobalCache(() => localforage); // uses localforage as the cache adapter
以及不同的每个路由适配器
const { adapter } = createCacheAdapter(() => localStorage); // uses localStorage as the cache adapter
const { adapter } = createCacheAdapter(() => sessionStorage); // uses sessionStorage as the cache adapter
const { adapter } = createCacheAdapter(() => localforage); // uses localforage as the cache adapter
假设您想使用一个使用数据库存储数据的自定义适配器。
您可以通过实现 CacheAdapter
接口并将其传递给 configureGlobalCache
或 createCacheAdapter
函数来做到这一点。
class DatabaseAdapter implements CacheAdapter {
async getItem(key: string) {
// get the item from the database
}
async setItem(key: string, value: string) {
// set the item in the database
}
async removeItem(key: string) {
// remove the item from the database
}
}
configureGlobalCache(() => new DatabaseAdapter()); // uses your custom adapter as the cache adapter globally
const { adapter } = createCacheAdapter(() => new DatabaseAdapter()); // uses your custom adapter as the cache adapter per route
API
createCacheAdapter
创建缓存适配器并返回它的函数。它接受一个参数,即用于存储数据的 adapter
。
import { createCacheAdapter } from "remix-client-cache";
const { adapter } = createCacheAdapter(() => localStorage); // uses localStorage as the cache adapter
configureGlobalCache
配置全局缓存适配器的函数。它接受一个参数,即用于存储数据的 adapter
。
import { configureGlobalCache } from "remix-client-cache";
configureGlobalCache(() => localStorage); // uses localStorage as the cache adapter
cacheClientLoader
用于缓存从加载器通过 clientLoader
导出传递到组件的数据。
它接受两个参数,第一个是传递给 clientLoader
函数的 ClientLoaderFunctionArgs
对象,第二个是具有以下属性的对象
type
- 它告诉客户端加载器是否应该使用正常的缓存机制(其中它存储数据并提前返回而不是重新获取)或是否应该使用staleWhileRevalidate
机制(其中它返回缓存的数据并在后台重新获取)。key
- 用于在缓存中存储数据的键。默认为当前路由路径,包括搜索参数和哈希。(例如,/user/1?name=John#profile)adapter
- 用于存储数据的缓存适配器。默认为库附带的内存适配器。
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { ClientLoaderFunctionArgs } from "@remix-run/react";
import { cacheClientLoader, useCachedLoaderData } from "remix-client-cache";
export const loader = async ({ params }: LoaderFunctionArgs) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${params.user}`
);
const user = await response.json();
await new Promise((resolve) => setTimeout(resolve, 1000));
return json({ user: { ...user, description: Math.random() } });
};
export const clientLoader = (args: ClientLoaderFunctionArgs) => cacheClientLoader(args, {
type: "swr", // default is swr, can also be set to normal
key: "/user/1" // default is the current route path including search params and hashes
adapter: () => localStorage // default is the in memory adapter, can be anything your wish
});
clientLoader.hydrate = true;
decacheClientLoader
用于删除从加载器通过 clientLoader
导出传递到组件的数据。
import { json, type LoaderFunctionArgs } from "@remix-run/node";
import { ClientLoaderFunctionArgs } from "@remix-run/react";
import { decacheClientLoader, useCachedLoaderData } from "remix-client-cache";
export const loader = async ({ params }: LoaderFunctionArgs) => {
const response = await fetch(
`https://jsonplaceholder.typicode.com/users/${params.user}`
);
const user = await response.json();
await new Promise((resolve) => setTimeout(resolve, 1000));
return json({ user: { ...user, description: Math.random() } });
};
// The data is cached here
export const clientLoader = (args: ClientLoaderFunctionArgs) => cacheClientLoader;
clientLoader.hydrate = true;
// It is de-cached after a successful action submission via the clientAction export
export const clientAction = decacheClientLoader;
接受一个可选对象,其中包含以下属性
key
- 用于在缓存中存储数据的键。adapter
- 用于存储数据的缓存适配器。
useCachedLoaderData
可用于从 clientLoader
导出获取缓存数据的钩子。必须与 cacheClientLoader
一起使用,因为从 cacheClientLoader
返回的数据已增强以考虑 useCachedLoaderData
,而不是标准的 useLoaderData
钩子。
import { useCachedLoaderData } from "remix-client-cache";
// Must be used together with cacheClientLoader
export const clientLoader = (args: ClientLoaderFunctionArgs) => cacheClientLoader(args, "swr");
clientLoader.hydrate = true;
export default function Index() {
// The data is automatically cached for you and hot swapped when refetched
const { user } = useCachedLoaderData<typeof loader>();
return (
<div>
{user.name} <hr /> {user.email}
<hr />
{user.username}
<hr />
{user.website} <hr />
{user.description}
</div>
);
}
接受一个可选对象,其中包含以下属性
-
adapter
- 用于存储数据的缓存适配器。默认为库附带的内存适配器。
useSwrData
用于获取为您热交换数据的 SWR 组件的钩子。它接受一个参数,即 useCachedLoaderData
或 useLoaderData
钩子返回的 loaderData。
import { useCachedLoaderData, useSwrData } from "remix-client-cache";
export const clientLoader = (args: ClientLoaderFunctionArgs) => cacheClientLoader(args);
clientLoader.hydrate = true;
export default function Index() {
// We do not destructure the data so we can pass in the object into the useSwrData hook
const loaderData = useLoaderData<typeof loader>();
// You can also use useCachedLoaderData hook with the useSwrData hook
const loaderData = useCachedLoaderData<typeof loader>();
// Pass the loader data into the hook and the component will handle everything else for you
const SWR = useSwrData(loaderData);
return (
<SWR>
{/** Hot swapped automatically */}
{({ user }) => (
<div>
{data.name} <hr /> {data.email}
<hr />
{data.username}
<hr />
{data.website} <hr />
{data.description}
</div>
)}
</SWR>
);
}
invalidateCache
可用于使特定键的缓存失效的实用程序函数。它接受一个参数,即用于在缓存中存储数据的 key
。也可以是键数组
import { invalidateCache } from "remix-client-cache";
invalidateCache("/user/1"); // invalidates the cache for the /user/1 route
请记住,这只能在客户端使用,因此可以在 clientLoader
或 clientAction
导出或组件本身中使用。
useCacheInvalidator
返回一个函数的钩子,该函数可用于使特定键的缓存失效。它接受一个参数,即用于在缓存中存储数据的 key
。也可以是键数组
import { useCacheInvalidator } from "remix-client-cache";
export default function Index() {
const { invalidateCache } = useCacheInvalidator();
return (
<div>
// invalidates the cache for the /user/1 route
<button onClick={ () => invalidateCache("/user/1") }>Invalidate cache</button>
</div>
);
}
支持
如果您喜欢该项目,请考虑通过在 Github 上点赞来支持我们。
许可证
MIT
错误
如果您发现错误,请在 GitHub 上的我们的问题跟踪器 中提交问题
贡献
感谢您考虑为 remix-client-cache 做出贡献!我们欢迎任何大小贡献,包括错误报告、功能请求、文档改进或代码更改。
要开始,请分叉此存储库并在新分支中进行更改。准备好提交更改后,请打开一个拉取请求,其中包含对更改的清晰描述以及任何相关问题或拉取请求。
请注意,所有贡献均受我们的 行为准则 约束。参与即表示您同意遵守此准则。
感谢您抽出时间为 remix-client-cache 做出贡献,并帮助使其成为社区中更好的工具!