
前端CMS【免费下载链接】wp-calypsoThe JavaScript and API powered WordPress.com项目地址https://gitcode.com/gh_mirrors/wp/wp-calypso点击查看免费下载本文基于 wp-calypso 仓库中 client/data/reader/use-feed-recommendations-mutation/README.md 编写深入讲解 Reader阅读器模块中用于管理推荐博客Recommended Blogs列表的自定义 React Hook。你将掌握它的 API 签名、乐观更新与失败自动回滚的完整数据流、底层 REST 端点以及它在订阅管理页与 Feed 详情页中的真实接入方式可直接照搬到自己的功能开发中。背景Reader Hooks 与推荐博客列表在 wp-calypso 中client/data/reader/目录集中存放着阅读器相关的数据层 Hook如 use-feed-recommendations-query、use-tag-stats.ts 等。其中use-feed-recommendations-mutation这个目录专用于管理当前用户推荐博客列表的成员状态——即把某个博客加入或移出用户自己的推荐列表。需要特别注意文档中的一句关键提示这里说的 sites 实际上就是 feeds。推荐列表的成员单位是 feedRSS/站点订阅源因此 Hook 的入参是feedId而不是siteId。在实现中这个列表被固化在一个名为recommended-blogs的阅读列表read list里常量定义于 index.tsconst RECOMMENDED_BLOGS_SLUG recommended-blogs;API 参考签名与返回值Hook 的完整签名定义在 index.tsexport const useFeedRecommendationsMutation ( feedId: number ): useFeedRecommendationsMutationResult { ... }参数参数类型说明feedIdnumber要管理推荐状态的 feed ID注意调用方常需用Number(...)转换因为很多数据源给出的是字符串 ID返回值useFeedRecommendationsMutationResultinterface useFeedRecommendationsMutationResult { isRecommended: boolean; // 当前推荐状态由 React Query 缓存推导 isUpdating: boolean; // 操作是否进行中 canToggle: boolean; // 是否允许切换决定开关是否可用 toggleRecommended: () void; // 切换推荐状态的函数 }四个返回值中isRecommended是只读状态、toggleRecommended是唯一动作配合isUpdating与canToggle两个护栏可以让消费组件无需关心任何请求细节。实战用法把推荐开关接到界面上README 给出的示例是一个订阅列表行组件用Toggle开关展示Recommended blog状态const SiteSubscriptionRow ( { feed_ID: feedId, /* other props */ } ) { const { isRecommended, toggleRecommended } useFeedRecommendationsMutation( Number( feedId ) ); return ( div classNamesubscription-row {/* Site info */} Toggle checked{ isRecommended } onChange{ toggleRecommended } labelRecommended blog / /div ); };这个示例在仓库中有两个真实落点值得对照阅读1. 订阅管理页的站点行client/landing/subscriptions/components/site-subscriptions-list/site-subscription-row.tsx// Use custom hook for recommended site functionality const { isRecommended, toggleRecommended } useFeedRecommendationsMutation( Number( feedId ?? 0 ) );这里需要注意一个真实场景的细节订阅接口返回的feed_id可能为空字符串因此调用方做了feedId ?? 0兜底再交给Number()转换。开关本体是wordpress/components的FormToggle并且用disabled{ ! currentUserName || typeof currentUserName ! string }与canToggle双保险地禁用了未登录状态见 site-subscription-row.tsx。切换时会先调用toggleRecommended()再通过recordRecommendToggle上报 Tracks 分析事件site-subscription-row.tsx。2. Reader Feed 详情页头部client/blocks/reader-feed-header/follow.tsxconst { isRecommended, isUpdating: isRecommendationPending, toggleRecommended, } useFeedRecommendationsMutation( feedId as number );在这里Hook 与封装好的 RecommendButton 组件配合按钮文案会在 Recommended 与 Recommend this blog 之间切换isLoading{ isRecommendationPending }在请求期间禁用按钮且只有当用户following || isRecommended时才展示推荐入口follow.tsx。状态如何计算三个布尔值的来源isRecommended完全由 React Query 缓存推导Hook 内部不维护任何本地 state推荐状态全部来自查询缓存index.tsconst { data: itemsData } useQuery( { ...readListItemsAllQuery( currentUserName, RECOMMENDED_BLOGS_SLUG ), enabled: !! currentUserName !! recommendedBlogsList?.ID, } ); const isRecommended !! itemsData?.items?.some( ( item ) Number( item.feed_ID ) Number( feedId ) );即只要recommended-blogs列表的 items 中存在feed_ID feedId的条目isRecommended即为true。由于乐观更新直接修改同一份缓存isRecommended会在点击瞬间翻转无需等待网络返回。canToggle前置条件检查canToggle 的计算要求同时满足两个条件const canToggle Boolean( currentUserName typeof currentUserName string recommendedBlogsList?.ID );存在已登录的当前用户名getCurrentUserName来自 Redux已找到当前用户自己的recommended-blogs列表具备ID。也就是说未登录用户、或尚未创建该列表的用户开关会被禁用。isUpdating任一请求进行中即锁定const { mutate: addFeed, isPending: isAdding } useMutation( addReadListFeedMutation( queryClient ) ); const { mutate: deleteFeed, isPending: isDeleting } useMutation( deleteReadListFeedMutation( queryClient ) ); const isUpdating isAdding || isDeleting;isUpdating用于防止并发切换——toggleRecommended内部第一道防线就是if ( ! canToggle || isUpdating || ... ) return;index.ts。行为机制乐观更新与失败自动回滚README 中写道useFeedRecommendationsMutationreads the recommended-blogs list items from React Query (readListItemsAllQuery) and toggles membership throughaddReadListFeedMutation/deleteReadListFeedMutation。Both mutations apply optimistic updates against the same query cache and roll back on failure, so the UI reverts automatically when the API call errors out.下面从源码拆解这条完整链路。数据读取两级查询Hook 先订阅当前用户的所有阅读列表index.tsconst { data: subscribedListsData } useQuery( { ...readSubscribedListsQuery(), enabled: !! currentUserName, } ); const recommendedBlogsList currentUserName ? subscribedListsData?.lists.find( ( list ) list.owner currentUserName list.slug RECOMMENDED_BLOGS_SLUG ) : undefined;readSubscribedListsQuery定义于 packages/api-queries/src/read-lists.ts其 queryKey 为[read, lists, subscribed]底层请求GET /read/lists时还携带了create_recommended_blogs_list: true参数——即首次拉取订阅列表时服务端会自动为用户创建 recommended-blogs 列表见 packages/api-core/src/read-lists/fetchers.ts。拿到recommendedBlogsList.ID之后才启用第二级查询读取列表全部条目const { data: itemsData } useQuery( { ...readListItemsAllQuery( currentUserName, RECOMMENDED_BLOGS_SLUG ), enabled: !! currentUserName !! recommendedBlogsList?.ID, } );readListItemsAllQuerypackages/api-queries/src/read-list-items.ts一次拉取 2000 条number: 2000并携带meta: site,feed,tag完整元数据对应GET /read/lists/{user}/{slug}/items见 packages/api-core/src/read-list-items/fetchers.ts。它还针对list_not_found用户尚无该列表时返回的永久 404做了特殊处理该错误不重试而普通瞬时错误最多重试 3 次与旧版>yarn test-client client/data/reader/use-feed-recommendations-mutation对应的测试文件是 client/data/reader/use-feed-recommendations-mutation/test/index.test.ts它用nock拦截真实 HTTP 请求通过renderHookQueryClientProviderReduxProvider组装出最小运行环境覆盖了五类关键行为初始非推荐状态列表不含该 feed 时isRecommended false且canToggle true已推荐状态列表含{ feed_ID: feedId }时isRecommended true无当前用户username null时canToggle false无 recommended-blogs 列表subscribedLists: []时canToggle false端点行为调用toggleRecommended()后分别断言POST /rest/v1.2/read/lists/testuser/recommended-blogs/feeds/new与POST /rest/v1.2/read/lists/testuser/recommended-blogs/feeds/{feedId}/delete确实被发出index.test.ts。测试中的mockList直接复刻了真实列表结构ID: 456、owner: testuser、slug: recommended-blogs而readListItemsAllQuery的缓存数据则按{ list_ID, success, items, page, number: 2000, total_items }的响应格式注入方便你在阅读测试时对照ReadListItemsResponse类型packages/api-core/src/read-list-items/types.ts。边界情况与使用注意事项综合 README 与源码实际接入时有几点需要留意feedId 必须为数字订阅接口等多处返回的是字符串 ID务必先Number(...)转换否则some比较与请求路径都会出错未登录 / 列表缺失时开关自动禁用canToggle已涵盖这两种情况无需消费方重复判断但订阅管理页仍额外叠加了disabled属性做双保险list_not_found不重试新用户首次进入时 recommended-blogs 列表可能尚未在缓存中readListItemsAllQuery对此做了专门的retry: false分支避免无意义的重复请求列表项是多态的ReadListItem可能携带feed_ID、site_ID或tag_ID乐观更新产生的占位条目可能只含其中一个字段所有字段均为可选/可空见 types.ts因此判断推荐状态时按feed_ID匹配是正确做法切换期间的防抖toggleRecommended在isUpdating为真时直接返回配合按钮的disabled{ isLoading }可避免快速连点导致的重复请求。小结useFeedRecommendationsMutation是 wp-calypso 数据层查询 乐观 mutation模式的代表性实现通过两级 React Query 查询订阅列表 → 列表条目推导只读状态通过共享同一 queryKey 的 add/delete mutation 实现即时 UI 反馈并在失败时用快照回滚。理解这条链路你就能在 Reader 相关功能中复用它也能参考 packages/api-queries/src/read-list-items.ts 的模式为自己的列表管理功能写出同样健壮的乐观更新代码。赞分享前端CMS【免费下载链接】wp-calypsoThe JavaScript and API powered WordPress.com项目地址https://gitcode.com/gh_mirrors/wp/wp-calypso点击查看免费下载相关推荐Qwen Code 多模型提供方配置完全指南modelProviders 与 providerProtocol 深度解析Qwen Code 多模型提供方配置完全指南modelProviders 与 providerProtocol 深度解析 导读 Qwen Code 是一款运行人工智能AI Agent代码智能体工具调用交互助手CLIQwenAria国际化支持多语言界面使用指南Aria国际化支持多语言界面使用指南 Aria作为一款简单高效的下载工具提供了全面的国际化支持让全球用户都能轻松使用母语界面进行操作。本文将详细介绍如何配Mobile-Agent跨平台GUI智能体的革命性突破Mobile Agent跨平台GUI智能体的革命性突破 在数字生活日益复杂的今天我们每天需要在多个设备间切换——手机处理社交消息电脑完成工作文档平板浏览人工智能大模型AI AgentGUI 自动化自主智能体上一篇如何解决会议记录漏记难题TMSpeech带来的实时语音转文字高效解决方案下一篇zteOnu中兴光猫命令行管理工具全解析创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考