跳到正文
cccskills
免费在 GitHub 公开中文介绍

data-fetching-architecture

指导使用 SWR 设计客户端数据获取,将 API 调用放入服务和状态钩子,并处理加载、错误及写入后的缓存刷新,也可协助迁移组件中的 `useEffect` 请求。

原文查看原文说明

Use for client APIs, SWR hooks, cache invalidation, async errors, useEffect migration, home first paint and persistent caches.

lobehub/lobehub星标 83,502(GitHub 83,502 + cccskills 0)

实测评分 未实测

查看安装方法

适用场景

  • 将组件请求移入状态钩子
  • 修复更新后的过期缓存
  • 整理加载、空结果和错误状态

中文介绍

功能

指导如何在服务、状态钩子和组件之间设计客户端数据获取。读取数据时使用 useClientDataSWR,并将 API 调用放在服务中。内容涵盖共享缓存键、写入后刷新数据、管理等待状态,以及区分首次加载、空结果和错误。也可用于将 useEffect 中的请求和组件内重复保存的数据迁移到状态钩子与选择器。对于从本地副本渲染的列表和详情,还会说明 Replica 数据的同步方式。

适用场景

当页面显示旧数据、请求似乎一直处于加载状态,或首次请求失败却显示为空结果时,可用此技能排查。也适用于整理数据请求应放置的位置。

使用示例

  • “把这个组件的数据请求迁移到状态中的 SWR 钩子。”
  • “查明更新后列表仍显示旧数据的原因。”
  • “说明如何区分首次请求失败和空结果。”

注意事项

实现应遵循 LobeHub 现有的封装和代码结构。需要查看实际封装配置,不能直接假定 SWR 的默认行为适用。

本介绍由 AI(OpenAI Codex)根据公开的 SKILL.md 撰写。准确的规格请查看下方原文。

文档检查

AI 根据 SKILL.md 的内容检查了需要注意的操作和使用前提。评分只依据实际运行的结果(实测对比)。

安全性发送数据、删除文件、执行命令等是否有说明
A良好资料说明乐观写入失败时的回滚和删除处理。
安装难易度所需工具和 API 密钥是否少且有说明
B部分不足依赖 SWR 和 LobeHub 封装,但未提供安装步骤。

不计入评分。 由 AI(OpenAI Codex)于 2026年10月26日 评估。仅供参考,安装前请同时查看原文。

如果您认为评估有误,请申请复核

请写明哪一项有误以及原因。无需登录。我们会对照 SKILL.md 重新评估,并在此处更新结果。

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

包含的文件(3)

  • SKILL.md7.0 KB
  • references/home-first-paint.md3.9 KB
  • references/walkthrough.md3.5 KB

SKILL.md(原文)

安装前可以查看交给 AI 代理的指令内容。

LobeHub Data Fetching Architecture

Component → Store useFetchXxx hook → Service → lambdaClient
          ← SWR request state + store data ← response

Layer boundaries

  • Services in src/services/ own API calls: .query() for reads, .mutate() for writes. Export a service instance per domain; components and stores do not call lambdaClient directly.
  • Store read hooks use useClientDataSWR and return its SWR response, including error and mutate. Sync successful results into the store through the wrapper's supported callback (onSuccess, or onData for the sync wrapper).
  • Server lists and details that paint from a local copy go through @/libs/replica instead: the store's useFetchXxx returns slice.useSync(params) (flags only) and the engine writes the store. See the Replica section of zustand.
  • Components call these hooks and read store data through selectors. Do not fetch in useEffect or duplicate server data in component useState.
  • Use useFetchXxx for read hooks and refreshXxx for cache invalidation.
  • For list/detail types, maps, reducers, and shared type sources, use Zustand data structures. For store layout and action classes, use zustand.

Home First Paint and Persistent Caches

When changing home/sidebar first paint or persisted display data, read references/home-first-paint.md. It covers avoiding flicker, reusing persistence, and the user displaySnapshot.ts boundary.

Cache keys and refresh

Reuse the domain key factory in src/libs/swr/keys.ts; define a shared key if the domain has none. Include every parameter that changes the response: entity or parent id, filters, sort, and pagination. Read and refresh must construct the same key.

Import useClientDataSWR and mutate from @/libs/swr, keeping the application's workspace/cache handling. Return a null key when a required id is absent; passing undefined to a store hook only disables fetching if that hook maps it to null.

For example, inside an action class:

useFetchBenchmarks = () =>
  useClientDataSWR(evalKeys.benchmarks(), () => agentEvalService.listBenchmarks(), {
    onSuccess: (data) => {
      this.#set({ benchmarkList: data, benchmarkListInit: true });
    },
  });

refreshBenchmarks = async () => {
  await mutate(evalKeys.benchmarks());
};

Read the current benchmark action for store wiring and the SWR wrappers for their actual options. These links locate implementation; they do not make every existing call site a template to copy unchanged.

Hooks that share a known parent id can run together. If one request needs a value returned by another, keep its key null until that value exists. Do not assume the application wrapper uses upstream SWR's default deduplication interval.

For lists cached separately under multiple parents, see parent-keyed lists. Ordinary flat lists need no extra layer.

Mutations

  • Call the service, then refresh affected list/detail keys and any related domain whose response changed. Await refresh when subsequent UI behavior depends on it.
  • Keep pending flags in store state and clear them in finally. Use per-id state for row updates/deletes so unrelated rows remain usable; create can use a separate flag because no persistent id exists yet.
  • For optimistic writes on replica data, use slice.optimistic / entity.optimistic; a rejected call rolls back on its own, deletes included.
  • For optimistic create/update on legacy stores, use the store's reducer/dispatch convention and restore or revalidate affected state if the service fails. Do not leave a temporary row or a successful-looking edit after rejection. Delete only after server success there: a hand-written reducer has no rollback base.
  • Let failures reach the caller's error UI; a finally block clears pending state but does not by itself recover an optimistic write.

Render loading, errors, and settled data

Consume the read hook's SWR response. Success-only flags such as isInit, missing map entries, and data ?? [] cannot distinguish an initial request failure from loading or empty results.

Use AsyncBoundary for standard loading/error/empty/data surfaces; use AsyncError for custom layouts and inline failures. Pass the original SWR data to the boundary: undefined means no successful result, whereas [] is a settled empty result.

Replica useSync returns no data. Feed AsyncBoundary its error and a settled signal from the store entry (data={isListInit || undefined}, where the selector is entry !== undefined); see src/features/Pages/PageLayout/Body/index.tsx.

const BenchmarkList = () => {
  const useFetchBenchmarks = useEvalStore((s) => s.useFetchBenchmarks);
  const benchmarks = useEvalStore((s) => s.benchmarkList);
  const { data, error, isLoading, mutate } = useFetchBenchmarks();

  return (
    <AsyncBoundary
      data={data}
      empty={<EmptyState />}
      error={error}
      isEmpty={data?.length === 0}
      isLoading={isLoading}
      onRetry={() => {
        void mutate();
      }}
    >
      <BenchmarkCards items={benchmarks} />
    </AsyncBoundary>
  );
};

The example's EmptyState and BenchmarkCards stand for the surface's existing renderers. Follow these distinctions when adapting it:

  • Show first-load errors before empty / NotFound / zero-value defaults. Do not put an error branch after if (!map[id]) return <Skeleton />; failure may never fill the map.
  • Preserve settled content during background revalidation failures, including a successfully loaded empty list. A retry in flight should show pending feedback.
  • In a fetched + static list, use the fetched slice's request state before combining rows. Static rows must not hide a failed request.
  • For infinite scroll, keep a per-bucket loadMoreError and show an inline Retry row. Suspend observer-triggered retries while that error is unresolved.
  • A closed modal or absent id disables a request; it is not evidence of a missing record.

Migrating an existing fetch

Move the API call into its service and the request into a store SWR hook. Reuse the existing state shape and action organization unless they need to change for the task. Replace the component's effect/local fetch state with that hook and selectors, then connect error, retry, and pending feedback. Refresh the same cache key after writes.

Check initial failure, retry, settled-empty results, background failure, and switching ids against the surface being changed. For stale data, compare read/refresh keys and the store bucket they update; for stuck loading, inspect rejected requests as well as success callbacks. Do not add a second fetch or another loading flag to mask the cause.

评价

还没有评价。欢迎分享你的使用感受。

评论

无需登录即可发表。欢迎分享使用感受、提问,或说明在哪些环境下无法运行。

还没有评论。来写第一条吧。

提交后会立即公开。请勿填写邮箱、电话等个人信息。URL 最多 2 个。

0 / 1000 字

此表单受 reCAPTCHA 保护,适用 Google 的隐私权政策和服务条款。

同一仓库的技能

acceptance

免费有简介

通过实际操作产品验证交付内容,记录用户可查看的证据,并发布验收报告。可检查已有验证计划或创建检查项,选择合适的操作界面,并使用 `lh` CLI 发布结果。

  • 实际检查更改后的产品行为
  • 收集截图或其他验证证据
  • 编写验证计划和报告
lobehub/lobehub8.4万2026年10月26日 更新

add-model-provider

免费有简介

本技能指导在 LobeHub 中接入 AI 模型提供商,涵盖运行时、模型信息、设置、品牌展示、文档和验收,并帮助判断新增模型是否需要新的提供商集成。

  • 为 LobeHub 接入新的 AI 模型提供商
  • 更新提供商设置说明或介绍
  • 判断添加模型是否需要新的提供商集成
lobehub/lobehub8.4万2026年10月26日 更新

add-setting-env

免费有简介

本技能指导如何用服务器端环境变量设置用户偏好默认值,涵盖变量校验、服务器配置、写入用户状态,以及更新 `.env.example` 和自托管文档。

  • 通过环境变量设置用户偏好默认值
  • 将设置接入服务器配置和用户状态
  • 更新环境变量示例及文档
lobehub/lobehub8.4万2026年10月26日 更新

agent-runtime-hooks

免费有简介

说明如何在智能体执行步骤、工具调用、人工审批、上下文压缩和子智能体调用时注册钩子。钩子可观察事件、模拟本地工具结果,也可通过 Webhook 按配置策略允许或拒绝工具调用。

  • 记录智能体步骤和错误
  • 测试时模拟工具结果
  • 通过 Webhook 允许或拒绝工具调用
lobehub/lobehub8.4万2026年10月26日 更新

agent-signal

免费有简介

此技能用于为智能体设计事件驱动的后台工作流,划分来源、信号、动作和策略的职责,并处理异步交接、去重、执行范围、完成记录与运行观测。

  • 添加事件触发的后台任务
  • 将工作移出对话请求
  • 防止动作重复执行
lobehub/lobehub8.4万2026年10月26日 更新

agent-testing-bot

免费有简介

本技能在 macOS 桌面聊天应用中测试机器人频道:打开应用、进入频道或联系人、发送消息、等待回复并截图。支持 Discord、Slack、Telegram、微信、飞书和 QQ;iMessage 使用 BlueBubbles 桥接。

  • 检查真实 Discord 频道中的机器人回复
  • 测试 Slack 或 Telegram 的对话流程
  • 记录 macOS 应用中的操作结果
lobehub/lobehub8.4万2026年10月26日 更新

查看 lobehub 的全部技能

报告此技能的问题