读取Apollo响应式变量的两种方式:哪种更具优势?
React/Apollo Client响应式变量两种取值方式的差异与选择
问题回顾
在学习用响应式变量做Apollo Client状态管理时,发现两种获取变量当前值的写法:
- 通过
useReactiveVar钩子:const myVar = useReactiveVar(myReactiveVar); - 直接调用响应式变量(不传参数):
const myVar = myReactiveVar();
想搞清楚这两种方式有没有优劣之分,以及背后的原因。另外我猜测这两种写法类似React中更新状态的两种方式(直接用状态vs传函数取前值),后者在异步场景下能保证拿到最新值,但不确定这个猜想对不对。
附上测试用的组件代码,两种方式在当前场景都能正常工作:
import React from 'react' import { useQuery, useReactiveVar } from '@apollo/client'; import { missionsLimitRV } from '../../apollo/client'; import { GET_MISSIONS } from '../../data/queries'; export const Missions = () => { const limit = useReactiveVar(missionsLimitRV); // 使用useReactiveVar取值 const { data, loading } = useQuery(GET_MISSIONS, { variables: { limit: limit } }); const addMission = () => { missionsLimitRV(missionsLimitRV() + 1) // 直接调用响应式变量取值 } if (loading) { return <h2>Loading...</h2> } if (!data.missions.length) { return <h2>No Missions Available</h2> } const missions = data.missions; console.log(missions); return ( <div> <button onClick={addMission}>add mission</button> { missions.map((mission) => ( <div key={mission.id}> <h2>{mission.name}</h2> <ul> {mission?.links?.map((link) => ( <li key={link}><a href={link}>{link}</a></li> ))} </ul> </div> )) } </div> ); };
两种方式的核心差异
1. 组件订阅与重渲染触发
useReactiveVar:这个钩子会让当前组件订阅响应式变量的变化。当变量的值更新时,组件会自动重渲染,确保UI和变量值保持同步。比如你的组件中useQuery依赖limit,用useReactiveVar拿到limit后,变量更新会触发组件重渲染,进而让useQuery重新发起请求,这正是你需要的效果。- 直接调用
myReactiveVar():只是同步获取调用瞬间的变量值,但不会让组件订阅变量变化。如果变量后续更新,组件不会因为这个变量的变化而重渲染,除非有其他触发重渲染的条件(比如组件自身state变化、父组件props更新)。
2. 异步场景下的取值逻辑
你的猜想有部分道理,但两者的差异和React setState的两种写法不完全相同:
- 在异步代码(比如
setTimeout、Promise回调)中,useReactiveVar拿到的是组件渲染时的变量值。如果变量在异步代码执行前更新,组件会重渲染,此时异步代码如果是在新的渲染周期里执行,能拿到最新值;但如果异步代码是捕获了旧渲染周期里的变量值,依然会拿到旧值。 - 直接调用
myReactiveVar()在异步代码中,每次调用都会实时读取变量的当前最新值,和组件是否重渲染无关。比如你在setTimeout(() => console.log(missionsLimitRV()), 1000)里调用,不管中间变量更新多少次,1秒后拿到的都是最新值。
3. 适用场景
- 需要变量变化驱动UI更新或查询执行:必须用
useReactiveVar。比如你的组件中limit变化后要重新拉取任务列表,这个场景下useReactiveVar是正确选择,因为它能触发组件重渲染,进而让useQuery重新执行。 - 仅需临时获取当前值(比如更新变量时的计算):用直接调用的方式更高效。比如你的
addMission函数里,只是取当前值加1再赋值,不需要触发组件额外重渲染,直接调用missionsLimitRV()就足够了。
总结
两种方式没有绝对的好坏,核心是看使用场景:
- 要让组件响应变量变化:选
useReactiveVar - 只需要当前值,不需要触发组件更新:直接调用响应式变量就行
内容的提问来源于stack exchange,提问作者Avi
相关产品推荐
相关产品推荐

