如何使用Apollo手动触发GraphQL查询 避免组件加载自动执行
问题根源
当前使用的useSendMailQuery是Apollo Client默认的查询Hook,默认行为为组件挂载时立即发起请求,因此会出现页面加载就自动触发邮件发送的问题。要实现手动触发请求,有两种可行方案:
方案1:改用Mutation实现(推荐,符合GraphQL规范)
发邮件属于带副作用的操作,按照GraphQL设计规范,这类操作本就应该定义在Mutation类型下,而非Query下,这是长期维护最合理的实现方式。
第一步:调整后端解析器
先把sendMail从Query字段移到Mutation字段下,同时建议等待邮件发送逻辑执行完成再返回结果,方便前端感知发送状态:
// Apollo server resolvers import { Resolvers } from "../generated/resolvers-types" import * as mail from "./nodemailer" export const resolvers: Resolvers = { Query: { getHello() { return 'hello' }, }, Mutation: { async sendMail() { try { await mail.main() return '发送成功' } catch (err) { return '发送失败' } } }, }
同步修改GraphQL Schema中sendMail的归属,从Query移到Mutation后,重新执行代码生成命令,更新generated目录下的类型和Hook文件。
第二步:前端替换为useMutation Hook
useMutation不会在组件挂载时自动执行,会返回一个手动触发的函数,你可以把它绑定到按钮点击等交互事件上:
import { FC } from 'react'; import './MyComponent.css'; import { useGetHelloQuery, useSendMailMutation } from '../../generated/queries' interface MyComponentProps {} const MyComponent: FC<MyComponentProps> = () => { const { data: hello, loading: isLoading } = useGetHelloQuery(); // 解构出触发函数和请求状态 const [sendMail, { loading: isMailing, data: mailResult }] = useSendMailMutation(); if (isLoading) { return <div>Loading...</div> } return ( <> <div className="MyComponent"> data: {hello?.getHello} <button disabled={isMailing} onClick={() => sendMail()} > {isMailing ? '邮件发送中...' : '点击发送邮件'} </button> {mailResult?.sendMail && <p>发送结果:{mailResult.sendMail}</p>} </div> </> ) } export default MyComponent
方案2:保留Query定义,使用useLazyQuery手动触发
如果暂时不想调整后端Schema,仍要把sendMail作为Query字段,可以把普通的useQuery替换为Apollo提供的useLazyQuery,这个Hook同样不会在组件挂载时自动执行,会返回手动触发的查询函数。
修改后的组件代码如下:
import { FC } from 'react'; import './MyComponent.css'; // 引入自动生成的lazy query Hook import { useGetHelloQuery, useSendMailLazyQuery } from '../../generated/queries' interface MyComponentProps {} const MyComponent: FC<MyComponentProps> = () => { const { data: hello, loading: isLoading } = useGetHelloQuery(); const [triggerSendMail, { loading: isMailing }] = useSendMailLazyQuery(); if (isLoading || isMailing) { return <div>Loading...</div> } return ( <> <div className="MyComponent"> data: {hello?.getHello} <button onClick={() => triggerSendMail()}>点击发送邮件</button> </div> </> ) } export default MyComponent
注意:该方案仅适合临时兼容场景。GraphQL规范中Query仅用于无副作用的数据查询,所有带副作用的操作(发邮件、增删改数据等)都应该使用Mutation实现,否则后续可能遇到缓存异常、请求意外重试等问题。
内容的提问来源于stack exchange,提问作者Peter Boomsma
相关产品推荐
相关产品推荐

