使用Apollo useLazyQuery仅在按钮点击时触发查询遇异常
解决useLazyQuery在抽屉组件中自动重复触发的问题
问题场景
在抽屉组件中使用Apollo的useLazyQuery,期望仅在用户点击按钮时执行查询,但每次关闭/重新打开抽屉时,查询都会自动触发。尝试过本地状态重置、调整fetchPolicy、组合useEffect/useState等方法均无效。
核心排查与解决方案
useLazyQuery的设计逻辑是仅在调用返回的触发函数时才执行查询,自动触发的问题基本都是代码逻辑疏漏导致的,从以下几点逐一排查修复:
1. 严格控制触发函数的调用时机
确保useLazyQuery返回的触发函数(比如示例中的getData)仅绑定在按钮点击事件中,绝对不能在组件渲染阶段直接调用:
- 错误写法(会在渲染时立即触发):
// 这里的getData()会在组件渲染时直接执行,而非点击时 <button onClick={getData({ variables: { id: 123 } })}>查询数据</button> - 正确写法:
const handleQuery = () => { getData({ variables: { id: 123 } }); }; <button onClick={handleQuery}>查询数据</button>
2. 排查useEffect中的意外调用
检查组件内是否存在useEffect在组件挂载时自动调用了触发函数,比如:
// 这种写法会在抽屉每次打开(组件挂载)时自动触发查询 useEffect(() => { getData(); }, []);
如果有此类代码,直接移除即可,确保查询仅由用户主动点击触发。
3. 不在useLazyQuery初始化配置中提前传variables
不要在useLazyQuery的初始配置里传入variables,而是在调用触发函数时动态传入。虽然官方文档说明useLazyQuery不会自动执行,但部分场景下提前传变量可能引发意外触发:
- 不推荐写法:
const [getData, { loading, data }] = useLazyQuery(GET_DATA, { variables: { id: 123 }, // 提前传入变量 fetchPolicy: 'network-only' }); - 推荐写法:
const [getData, { loading, data }] = useLazyQuery(GET_DATA, { fetchPolicy: 'network-only' }); // 点击时传入变量并触发查询 const handleQuery = () => { getData({ variables: { id: 123 } }); };
4. 确保抽屉组件的卸载逻辑正常
如果抽屉是通过display: none或visibility: hidden隐藏而非完全卸载,组件会一直保留在DOM中,可能因状态残留导致查询重复触发。此时可以给抽屉组件添加唯一key,确保每次打开时重新初始化组件状态:
<Drawer key={Date.now()} open={isOpen} onClose={handleClose}> {/* 组件内容 */} </Drawer>
5. 检查Apollo Client版本
部分旧版本的Apollo Client存在useLazyQuery自动触发的bug,尝试更新到@apollo/client@3.x的最新稳定版本。
验证后的完整示例代码
import { useLazyQuery } from '@apollo/client'; import { GET_DATA } from './your-queries'; const DrawerComponent = ({ isOpen, onClose }) => { const [getData, { loading, data, error }] = useLazyQuery(GET_DATA, { onCompleted: () => { // 查询完成后的业务逻辑 }, onError: (e) => { // 错误处理逻辑 }, context: { clientName: 'search' }, fetchPolicy: 'network-only' }); const handleQueryClick = () => { // 仅在用户点击时传入变量并触发查询 getData({ variables: { /* 你的查询变量 */ } }); }; if (!isOpen) return null; return ( <div className="drawer"> <button onClick={handleQueryClick} disabled={loading}> {loading ? '查询中...' : '获取数据'} </button> {/* 数据展示区域 */} {data && <div>{JSON.stringify(data)}</div>} {error && <div className="error">查询失败:{error.message}</div>} <button onClick={onClose}>关闭抽屉</button> </div> ); }; export default DrawerComponent;
内容的提问来源于stack exchange,提问作者pitchdiesel
相关产品推荐
相关产品推荐

