Apollo Client响应式变量更新后查询未自动刷新的问题排查
Apollo Client响应式变量更新后查询未自动刷新的问题排查
哎,我瞅了你的代码,发现两个核心问题导致点击“Load More”后查询没自动刷新,咱们一步步来解决:
问题1:响应式变量定义在组件内部,每次渲染都重建
你现在把makeVar放在了RecipeList组件函数里,这意味着每次组件重新渲染时,都会创建全新的queryLimit和pagerOffset变量。之前更新的变量和useQuery绑定的根本不是同一个实例,自然不会触发查询更新。
问题2:组件没订阅响应式变量的变化
你的useQuery里的variables是直接调用queryLimit()和pagerOffset()获取初始值,但组件并没有订阅这些变量的变化——变量更新时,组件不会重新渲染,useQuery也就不知道要重新执行查询。
修正后的完整代码
第一步:App.js(原代码逻辑没问题,确认即可)
// App.js import { InMemoryCache } from '@apollo/client'; import { offsetLimitPagination } from "@apollo/client/utilities"; const queryCache = new InMemoryCache({ typePolicies: { Query: { fields: { entries: offsetLimitPagination(["section"]) // 与查询的section变量对应,缓存策略正确 } } } });
第二步:重构RecipeList.js
// RecipeList.js import { makeVar, useQuery, gql, useReactiveVar } from '@apollo/client'; // !!关键:把响应式变量移到组件外部,确保全局唯一实例 export const queryLimit = makeVar(12); export const pagerOffset = makeVar(0); const GET_RECIPE_ENTRIES = gql` query GetRecipeEntries( $section: [String] $limit: Int $offset: Int ) { recipeList: entries( section: $section limit: $limit offset: $offset ) { id title uri ... on recipes_recipes_Entry { recipeImg { url } } } } `; export default function RecipeList() { // 用useReactiveVar订阅变量变化,变量更新时组件自动重新渲染 const limit = useReactiveVar(queryLimit); const offset = useReactiveVar(pagerOffset); const { data } = useQuery(GET_RECIPE_ENTRIES, { variables: { section: ["recipes"], limit, // 使用订阅后的实时变量值 offset, }, // 可选:如果需要强制每次变量变化都请求后端(而非仅读缓存),可以添加 // fetchPolicy: 'cache-and-network' }); const renderRecipeList = () => { if (data) { return ( <ul> {data.recipeList.map((recipe) => ( <li key={recipe.id}> <a href={recipe.uri}> <h3>{recipe.title}</h3> <img src={recipe.recipeImg[0].url} alt={recipe.title} /> </a> </li> ))} </ul> ); } }; const handleClick = () => { // 更新响应式变量,组件会因useReactiveVar的订阅自动重新渲染,触发useQuery执行新查询 pagerOffset(pagerOffset() + queryLimit()); }; const renderMoreButton = () => { return <button onClick={handleClick}>Load More</button>; }; return ( <div> {renderRecipeList()} {renderMoreButton()} </div> ); }
为啥这样改就生效?
- 响应式变量全局化:放在组件外后,整个应用生命周期内只会创建一次,更新和查询用的是同一个变量实例。
- useReactiveVar订阅变化:
useReactiveVar会让组件监听响应式变量的更新,变量一变,组件就重新渲染,useQuery检测到variables里的limit/offset值变化,就会自动执行新的查询。 - 你的
offsetLimitPagination缓存策略是对的,它会帮你把新分页的条目合并到缓存里,不会覆盖之前的结果。
额外小提示
如果需要在其他组件里控制分页(比如顶部的分页器组件),直接导入queryLimit和pagerOffset,调用queryLimit(newLimit)或者pagerOffset(newOffset)就能更新,RecipeList组件会自动响应变化~
内容来源于stack exchange




