WordPress.com 阅读器推荐博客开关解析:`useFeedRecommendationsMutation` Hook 实战指南
前端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),仅供参考

相关新闻

绕过输入框禁止粘贴:模拟键盘输入的自动化脚本方案

绕过输入框禁止粘贴:模拟键盘输入的自动化脚本方案

1. 输入框禁止粘贴的底层逻辑与破局思路1.1 为什么有些输入框就是不让粘贴做过自动化工具的人几乎都遇到过这种场景:某个系统的登录页、报名表单或者后台管理界面,密码框和确认密码框死活不接受右键粘贴,甚至连CtrlV都被拦截。你手动敲一遍没…

2026/9/25 8:12:30 阅读更多 →
如何实现闲鱼批量抓取采集自动化?接口层直取数据,比传统爬虫快10倍

如何实现闲鱼批量抓取采集自动化?接口层直取数据,比传统爬虫快10倍

如何实现闲鱼批量抓取采集自动化?接口层直取数据,比传统爬虫快10倍 电商这行,谁的速度快谁吃肉。闲鱼的批量抓取采集,是店群运营中最耗人力也最容易出错的环节。 采集竞品数据是店群运营的命脉。但各大平台的反爬系统越来越强&…

2026/9/25 8:12:30 阅读更多 →
Windows Installer服务异常与UIPI权限导致安装无界面

Windows Installer服务异常与UIPI权限导致安装无界面

1. 这不是“软件打不开”,而是Windows Installer服务被静默劫持了你点开一个.exe或.msi安装包,鼠标转圈两秒,然后——什么都没发生。任务栏没弹窗,进程列表里找不到setup.exe,连错误提示都不给一个。你反复双击、右键“…

2026/9/25 8:12:30 阅读更多 →

最新新闻

Atlas 300V 24G推理加速卡解析与YOLO部署实战

Atlas 300V 24G推理加速卡解析与YOLO部署实战

这几天在技术社区刷到一个挺典型的提问:atlas 300v 24g 是运算加速卡吗。底下回答有说算的,有说只是推理卡的,还有直接丢官网链接的,但基本都没讲到点子上。结合另一个热搜词"atlas部署yolo",我猜很多朋友其…

2026/9/25 8:46:08 阅读更多 →
奈奎斯特定理与香农定理:采样率与信道容量如何决定系统上限

奈奎斯特定理与香农定理:采样率与信道容量如何决定系统上限

干过通信、音频、图像处理的朋友,应该都绕不开这两条定理。不管是设计基站的物理层,还是调一套音频采样链路,最终都会被问到同一个问题:你这个系统的上限在哪?要回答这个问题,就要回到奈奎斯特定理和香农定…

2026/9/25 8:46:08 阅读更多 →
Win10任务栏图标空白?从缓存重建到系统组件修复全指南

Win10任务栏图标空白?从缓存重建到系统组件修复全指南

打开电脑,发现任务栏上某个软件的图标变成了一块白色空白,鼠标悬停还有标题提示,点一下也能正常打开窗口,但图标就是“失联”了;更惨的情况是整个任务栏右侧的托盘图标、中间打开的APP图标全部消失,只剩一个…

2026/9/25 8:46:08 阅读更多 →
3GPP Rel-15/16/17规范下载全攻略:官网入口、FTP目录与版本选择

3GPP Rel-15/16/17规范下载全攻略:官网入口、FTP目录与版本选择

干通信这行的,几乎每天都要跟3GPP的规范打交道。Rel-15、Rel-16、Rel-17这三个版本,可以说是最近几年所有5G相关项目的“地基”,从物理层算法到协议栈代码,从终端一致性测试到核心网流程设计,全得靠这些文档说话。可最…

2026/9/25 8:46:08 阅读更多 →
ax调度:面向agentic工作负载的Kubernetes CLI编排实践

ax调度:面向agentic工作负载的Kubernetes CLI编排实践

1. 从“ax”这个名字说起:一个被低估的调度入口第一次看到“ax”这个标题,很多人会以为是某个命令行工具的缩写,或者某个内部项目的代号。但把热搜词摊开来看——agentic、orchestrator、Kubernetes、CLI、ax调度——这几个词拼在一起&#x…

2026/9/25 8:46:08 阅读更多 →
Oracle到瀚高数据库数据抽取工具:从类型映射到断点续传

Oracle到瀚高数据库数据抽取工具:从类型映射到断点续传

简介:面向 Oracle 与瀚高(HGDB)数据库运维及数据迁移人员,这份资源提供可直接运行的数据库抽取工具,用于将 Oracle 中的数据抽取、转换后导入瀚高,或实现两库间的定期同步,重点解决异构迁移时的…

2026/9/25 8:45:07 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →