Relay 网络层错误处理实战:useMutationAction_EXPERIMENTAL 的 try/catch 捕获与降级策略
Relay 网络层错误处理实战useMutationAction_EXPERIMENTAL 的 try/catch 捕获与降级策略【免费下载链接】relayRelay is a JavaScript framework for building>项目地址: https://gitcode.com/gh_mirrors/relay29/relay导读本文以 useMutationAction-network-error-catch.md 这份端到端测试 fixture 为核心讲解 Relay 新一代 mutation HookuseMutationAction_EXPERIMENTAL在网络层失败时的错误处理模式当网络请求本身失败时commitAction返回的 Promise 会reject组件可在startTransition内用try/catch捕获异常并就地展示错误信息完全无需依赖 Error Boundary。读完本文你将掌握useMutationAction_EXPERIMENTAL的完整用法、它的 Promise 化底层实现原理以及网络错误与字段级错误catch(to: RESULT)两种失败形态的正确区分与处理策略。一、背景useMutationAction_EXPERIMENTAL 是什么useMutationAction_EXPERIMENTAL是 react-relay 提供的一个实验性 Hook它是useMutation的一个变体专门适配 React 的Action 模式React 19 的 form action /startTransitionasync 回调。它返回一个异步 action 函数commitAction调用commitAction(variables)触发一次 mutation返回PromiseTDatamutation 成功后 resolve 为响应数据网络层失败或onError触发时reject调用方可以捕获。从类型声明 useMutationAction_EXPERIMENTAL.d.ts 可以确认其签名export function useMutationAction_EXPERIMENTALTMutation extends OperationType( mutation: GraphQLTaggedNode, ): (variables: VariablesOfTMutation) PromiseTMutation[response];注意一个设计细节这里约束的是OperationTyperesponse 为unknown而非更严格的MutationParametersresponse 为Recordstring, unknown。源码注释解释了这个取舍——更严格的类型会拒绝catchmutation 的Result…联合类型响应为了兼容现代 Hook 保持一致类型上做了放宽见 d.ts 注释。二、网络层失败时发生了什么Promise reject 的底层原理理解本文主题前先看 useMutationAction_EXPERIMENTAL.js 的核心实现——它本身并不发明新的错误语义而是把传统commitMutation的两种回调包装成 Promiseconst commitAction useCallback( (variables: TVariables): PromiseTData { return new Promise((resolve, reject) { commitMutation(environment, { mutation, variables, onCompleted: (response: TData) { resolve(response); }, onError: (error: Error) { reject(error); }, }); }); }, [environment, mutation], );可以清晰看到两条错误/成功通路成功通路GraphQL 请求完成且无错误 →onCompleted被调用 →resolve(response)失败通路网络层本身失败请求没发出去、超时、HTTP 5xx、传输层异常等→commitMutation触发onError(error)→reject(error)。也就是说网络错误的暴露方式就是一个被 reject 的 Promise。本文 fixture 的核心结论由此而来组件拿到这个 Promise 后在 transition 内部用try/catch包裹await commitAction(...)即可在不引入 Error Boundary 的前提下捕获网络故障并渲染错误文案。三、完整场景拆解一个查询正常、突变失败的测试环境本 fixture 精心构造了一个不对称的网络环境用来精确隔离网络层失败这一种故障模式。3.1 配置文件relay.config.jsonfixture 中的 Relay 编译配置为标准三件套{ src: ./, schema: ./schema.graphql, language: typescript }src: ./以 fixture 目录为源码根递归收集 GraphQL 定义与组件代码schema: ./schema.graphql指向由 Grats 从服务端代码推导出的 schemalanguage: typescript生成__generated__/*.graphql.ts类型产物测试中组件直接 import 这些类型。3.2 服务端定义server.tsGrats 风格服务端用 Grats 的 JSDoc 指令声明 GraphQL 字段/** gqlQueryField */ export function greeting(): string { return Ready; } /** gqlMutationField */ export function doSomething(args: { input: string }): string { return ok; }这里greeting用于初始查询保证页面首屏渲染出 ReadydoSomething是待测试的 mutation——它本身是正常的真正故障来自客户端网络层见下文这保证了故障点被精确控制在传输层。3.3 混合网络层让 query 走真实网络、mutation 必然失败fixture 的核心技巧在App.tsx中构造了一个按操作类型分流的网络const failingMutationNetwork Network.create((operation, variables) { if (operation.operationKind mutation) { return Observable.create((sink) { sink.error(new Error(Network request failed)); }); } return gratsNetwork.execute(operation, variables, {}, null); }); const testEnvironment new Environment({ network: failingMutationNetwork });对mutation返回一个立即sink.error(new Error(Network request failed))的 Observable模拟请求发出前就失败的极端网络故障对其他操作这里是初始 query透传给 gratsNetwork一个基于graphql库execute/subscribe的内存 GraphQL 服务见 GratsNetwork.ts。这个分流网络的价值在于它把一个复杂问题mutation 网络故障在测试环境里变成了确定性的、可重复触发的事件同时让查询路径保持真实可用。四、组件实现transition 内的 try/catch 捕获完整组件代码如下与 fixture 一致import { Suspense, useState, useTransition } from react; import { RelayEnvironmentProvider, useLazyLoadQuery, useMutationAction_EXPERIMENTAL, } from react-relay; import { graphql, Environment, Network, Observable } from relay-runtime; import { gratsNetwork } from ../GratsNetwork; import { AppTestQuery } from ./__generated__/AppTestQuery.graphql; import { AppDoSomethingMutation } from ./__generated__/AppDoSomethingMutation.graphql; const failingMutationNetwork Network.create((operation, variables) { if (operation.operationKind mutation) { return Observable.create((sink) { sink.error(new Error(Network request failed)); }); } return gratsNetwork.execute(operation, variables, {}, null); }); const testEnvironment new Environment({ network: failingMutationNetwork }); function Content() { const data useLazyLoadQueryAppTestQuery( graphql query AppTestQuery { greeting } , {}, ); const commitAction useMutationAction_EXPERIMENTALAppDoSomethingMutation( graphql mutation AppDoSomethingMutation($input: String!) { doSomething(input: $input) } , ); const [errorMessage, setErrorMessage] useStatestring | null(null); const [isPending, startTransition] useTransition(); return ( div div{data.greeting}/div button disabled{isPending} onClick{() { startTransition(async () { try { await commitAction({ input: test }); } catch (err) { setErrorMessage((err as Error).message); } }); }} Submit /button {errorMessage ! null divCaught error: {errorMessage}/div} /div ); } export default function TestApp() { return ( RelayEnvironmentProvider environment{testEnvironment} Suspense fallback{divLoading.../div} Content / /Suspense /RelayEnvironmentProvider ); }逐段拆解这个模式4.1 数据声明与 mutation 注册useLazyLoadQueryAppTestQuery负责首屏查询greeting结果渲染在div{data.greeting}/divuseMutationAction_EXPERIMENTALAppDoSomethingMutation注册 mutation返回commitAction。注意泛型参数用的是编译生成的AppDoSomethingMutation类型来自__generated__/目录这样commitAction({ input: test })的参数会被静态类型检查。4.2 核心await try/catch setState点击按钮后在startTransition(async () {...})内try { await commitAction({ input: test }); } catch (err) { setErrorMessage((err as Error).message); }await commitAction(...)挂起在 mutation 的网络请求上一旦网络层失败底层onError → reject使await抛出异常进入catch分支通过setErrorMessage把错误写入本地 state组件随后渲染divCaught error: {errorMessage}/diverr as Error是因为catch到的值类型是unknown这里断言为Error以读取.message。4.3 与 Error Boundary 方案的对照值得强调的是本 fixture 与同目录下的姊妹 fixture useMutationAction-network-error-boundary.md 构成了同一故障的两种处理姿势处理方式错误去向适用场景transition 内 try/catch本文捕获后写入本地 state就地展示局部、可恢复、希望保留页面上下文Error Boundary姊妹 fixture不捕获React 将 rejection 冒泡到最近的 error boundary全局兜底、需要卸载子树并展示全屏错误页两份 fixture 的网络层构造、服务端定义完全一致差异仅在onClick中是否包裹try/catch以及根节点是否挂ErrorBoundary——这为读者提供了可对照的实验素材。4.4 isPending 的作用const [isPending, startTransition] useTransition()中的isPending用于在 transition 进行中禁用按钮disabled{isPending}避免用户重复提交。它是 React 19 async transition 的标准配套await commitAction期间isPending为truePromise 结算后自动回到false——即使请求失败isPending也会被正确复位UI 不会卡死在 pending 状态。五、运行验证markdown 驱动的端到端测试5.1 交互步骤 DSLfixture 末尾用steps代码块声明断言序列wait Ready click button Submit wait Caught error: Network request failed对应 runInteractions.js 中实现的交互 DSLwait Ready等待文本 Ready 出现对应首屏查询渲染完成click button Submit通过getByRole(button, {name: Submit})定位并user.clickwait Caught error: Network request failed等待错误文案出现验证 try/catch 分支确实把Error(Network request failed)的消息渲染了出来。这个第三行断言正是本 fixture 的验收标准如果commitAction的 rejection 没有被捕获页面将不会出现 Caught error: ... 文本测试即失败。5.2 快照验证对应的快照 useMutationAction-network-error-catch.snap.md 记录了交互后的最终 DOMdivdivReady/divbuttonSubmit/buttondivCaught error: Network request failed/div/div可以看到三个要素齐备初始数据Ready、按钮Submit、以及被捕获并展示的网络错误文案——完整印证了无 Error Boundary 也能就地展示网络错误这一结论。5.3 如何运行根据 relay-e2e-test 说明文档从仓库根目录执行yarn test:e2e单独运行本 fixtureyarn test:e2e -- --testNamePattern network-error-catch前置条件首次需在packages/relay-e2e-test下yarn install并先yarn build构建 babel-plugin-relay。编译器解析顺序为RELAY_COMPILER_BINARY环境变量 → 本地 cargo 构建的compiler/target/debug/relay→ npm 的relay-compiler兜底。测试会提取 markdown 中的代码块经 Grats relay-compiler 编译、tsc类型检查后用 React Testing Library 渲染并执行交互步骤、快照对比改动快照需yarn test:e2e -u重写。六、错误谱系try/catch 与 catch 各管一段理解本 fixture 语义的关键是不要把网络错误与字段级错误混为一谈。同目录的另一份 fixture useMutationAction-catch-field-error.md 给出了精确定义网络错误传输层失败请求未完成。commitAction的 Promisereject只能用try/catch或 Error Boundary处理。这是本文的主题。字段级错误请求成功返回但 GraphQL 响应中某个字段解析失败。默认情况下字段会静默变为null调用方无法区分null 是因为报错还是null 是因为本来就 null。字段级错误的现代解法是catch(to: RESULT)指令把字段响应包装成{ok: true, value: ...} | {ok: false, errors: [...]}联合类型调用方通过检查result.ok判断不需要 try/catch。如该 fixture 所示mutation AppFailingMutationMutation($input: String!) { failingMutation(input: $input) catch(to: RESULT) }随后代码中if (result.ok) {...} else { setMessage(Field error caught via catch); }即可分支处理而最外层catch (err)仍只负责网络错误。这也解释了为何useMutationAction_EXPERIMENTAL的 Promise 语义与catch天然互补前者承载请求层面的失败后者承载数据层面的失败两者共同覆盖了 mutation 的完整失败谱系。七、总结与最佳实践从本 fixture 及其源码实现可以提炼出useMutationAction_EXPERIMENTAL网络错误的完整处理清单理解 Promise 语义commitAction是对commitMutation的 Promise 化包装源码onError → reject、onCompleted → resolve网络失败必然表现为 rejection就地处理用 try/catch把await commitAction(...)放在startTransition(async () {...})内catch中读取(err as Error).message并setState展示无需 Error Boundary需要全局兜底时用 Error Boundary对比 useMutationAction-network-error-boundary.md 的实现不捕获的 rejection 会被 React 冒泡到最近的 error boundary区分两种错误try/catch只处理网络错误字段级错误应使用catch(to: RESULT)检查result.ok见 useMutationAction-catch-field-error.md用isPending防重复提交transition 期间禁用按钮请求失败后isPending也会自动复位可复现测试通过按operationKind分流的自定义 Networkmutation 立即sink.error、query 走真实网络即可在测试环境中确定性触发网络故障配合stepsDSL 断言错误文案的渲染形成闭环验证。这种query 正常、mutation 失败的分流网络构造是测试 Relay 应用网络错误处理最实用的基础设施之一——它不需要 mock 整个服务器只定向破坏你想验证的那条路径让错误处理的每条分支都变得可测试、可回归。【免费下载链接】relayRelay is a JavaScript framework for building>项目地址: https://gitcode.com/gh_mirrors/relay29/relay创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Less.js混合器完全教程:Mixin参数、Guards与嵌套选择器实战

Less.js混合器完全教程:Mixin参数、Guards与嵌套选择器实战

Less.js混合器完全教程:Mixin参数、Guards与嵌套选择器实战 【免费下载链接】less.js Less. The dynamic stylesheet language. 项目地址: https://gitcode.com/gh_mirrors/le/less.js Less.js 是动态样式表语言(The dynamic stylesheet language…

2026/9/21 2:56:36 阅读更多 →
node-redis 支持哪些 Redis 版本?官方兼容性矩阵与 CI 验证机制全解析

node-redis 支持哪些 Redis 版本?官方兼容性矩阵与 CI 验证机制全解析

后端数据库客户端缓存 【免费下载链接】node-redis Redis Node.js client 项目地址: https://gitcode.com/gh_mirrors/no/node-redis 点击查看 免费下载 Node Redis(redis npm 包)是面向 Node.js 的高性能 Redis 客户端。在生产环境选型时&a…

2026/9/21 2:56:36 阅读更多 →
AirSim安全系统详解:地理围栏与障碍物地图配置完整指南

AirSim安全系统详解:地理围栏与障碍物地图配置完整指南

AirSim安全系统详解:地理围栏与障碍物地图配置完整指南 【免费下载链接】AirSim Open source simulator for autonomous vehicles built on Unreal Engine / Unity, from Microsoft AI & Research 项目地址: https://gitcode.com/gh_mirrors/ai/AirSim A…

2026/9/21 2:56:36 阅读更多 →

最新新闻

claude-seo 实战:用 FLOW 框架 Audience Avatar 提示词构建可执行的「Find」阶段受众画像交付物

claude-seo 实战:用 FLOW 框架 Audience Avatar 提示词构建可执行的「Find」阶段受众画像交付物

claude-seo 实战:用 FLOW 框架 Audience Avatar 提示词构建可执行的「Find」阶段受众画像交付物 【免费下载链接】claude-seo Universal SEO skill for Claude Code. 25 sub-skills 18 sub-agents covering technical SEO, E-E-A-T, schema, GEO/AEO, backlinks, l…

2026/9/21 3:25:55 阅读更多 →
MXNet Profiler 性能剖析实战:官方示例逐行拆解与底层实现原理

MXNet Profiler 性能剖析实战:官方示例逐行拆解与底层实现原理

人工智能深度学习机器学习 【免费下载链接】mxnet Lightweight, Portable, Flexible Distributed/Mobile Deep Learning with Dynamic, Mutation-aware Dataflow Dep Scheduler; for Python, R, Julia, Scala, Go, Javascript and more 项目地址: https://gitcode.c…

2026/9/21 3:25:54 阅读更多 →
lark-cli `apps +init` 实战指南:妙搭(Spark/Miaoda)应用本地开发环境的完整初始化流程

lark-cli `apps +init` 实战指南:妙搭(Spark/Miaoda)应用本地开发环境的完整初始化流程

CLIAI 技能 【免费下载链接】cli The official Lark/飞书 CLI tool, maintained by the larksuite team — built for humans and AI Agents. Covers core business domains including Messenger, Docs, Base, Sheets, Calendar, Mail, Tasks, Meetings, and more, with 200 co…

2026/9/21 3:24:54 阅读更多 →
Prettier 对 Markdown Front-Matter 中 Unicode 内容的处理机制与测试验证

Prettier 对 Markdown Front-Matter 中 Unicode 内容的处理机制与测试验证

开发工具格式化CLI 【免费下载链接】prettier Prettier is an opinionated code formatter. 项目地址: https://gitcode.com/gh_mirrors/pr/prettier 点击查看 免费下载 Prettier 在格式化 Markdown 文档时,会识别并完整保留文件头部的 YAML/TOML Front…

2026/9/21 3:24:54 阅读更多 →
FinRL-Meta 基准评测体系指南:统一绩效指标、基线策略与回测实战

FinRL-Meta 基准评测体系指南:统一绩效指标、基线策略与回测实战

FinRL-Meta 基准评测体系指南:统一绩效指标、基线策略与回测实战 【免费下载链接】FinRL FinRL: Financial Reinforcement Learning. 🔥 项目地址: https://gitcode.com/gh_mirrors/fi/FinRL-Library 导读 本文以 FinRL-Meta 的 Benchmark 文档&…

2026/9/21 3:23:54 阅读更多 →
低功耗Bandgap设计实战:结构、启动电路与验证方法

低功耗Bandgap设计实战:结构、启动电路与验证方法

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/21 3:22:53 阅读更多 →

日新闻

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程

agents-generator 决策矩阵全解析:从项目检测到 AGENTS.md 规则生成的 16 步判定流程 【免费下载链接】agentic-awesome-skills AAS Core is the local, agent-first control plane for complete catalog discovery, agent-owned selection, stack validation, and …

2026/9/21 0:00:01 阅读更多 →
gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析

gin-vue-admin 前端工具函数全景指南:src/utils 复用规范与源码级解析 【免费下载链接】gin-vue-admin 🚀ViteVue3Gin拥有AI辅助的基础开发平台,企业级业务AI开发解决方案,内置mcp辅助服务,内置skills管理,…

2026/9/21 0:00:01 阅读更多 →
Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

Wox 全功能插件开发实战指南:基于 Python / Node.js 宿主与 WebSocket 的持久化插件体系

桌面应用AI 应用插件系统 【免费下载链接】Wox A cross-platform launcher that simply works 项目地址: https://gitcode.com/gh_mirrors/wo/Wox 点击查看 免费下载 全功能插件(Full-featured Plugin)是 Wox 三类插件实现方式中能力最完整的…

2026/9/21 0:00:01 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/9/20 0:00:46 阅读更多 →

月新闻

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

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

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

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

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

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

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

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

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

2026/9/19 23:35:34 阅读更多 →