如何在React Apollo 2.0中正确重取并缓存更新数据,解决旧数据残留?
嘿,这个问题我太熟了!看起来你是依赖缓存里的邮箱去拉取用户数据,修改后明明重新请求了服务器,但客户端就是抱着旧缓存不放,非得重启或者client.resetStore()才肯更新,对吧?别慌,给你几个针对性的解决办法,从简单到彻底的都有:
1. 手动更新缓存(精准控制)
当你执行修改用户数据的mutation之后,直接手动把新数据塞进对应的缓存条目里。比如你获取用户的查询叫GetUserByEmail,那就在mutation的update函数里做这件事:
const [updateUser] = useMutation(UPDATE_USER_MUTATION, { update(cache, { data: { updateUser } }) { // 先读出缓存里的旧用户数据 const existingUser = cache.readQuery({ query: GET_USER_BY_EMAIL, variables: { email: updateUser.email } // 要和你之前查询用的变量完全一致 }); // 把新数据写回缓存,覆盖旧的 cache.writeQuery({ query: GET_USER_BY_EMAIL, variables: { email: updateUser.email }, data: { getUserByEmail: updateUser } }); } });
这么做之后,缓存会立刻更新,UI也会同步渲染新内容,完全不用等重启。
2. 用refetchQueries强制重取
如果你不想手动操作缓存,还有个更简单的办法——在mutation里指定refetchQueries,让客户端自动重新执行你的用户查询,用最新的服务器数据覆盖缓存:
const [updateUser] = useMutation(UPDATE_USER_MUTATION, { refetchQueries: [ { query: GET_USER_BY_EMAIL, variables: { email: currentCachedEmail } } // 变量要对应缓存里的邮箱 ] });
这个方法简单粗暴,但要注意如果你的查询比较复杂,可能会有一点点性能开销,不过大部分场景下完全没问题。
3. 检查缓存归一化配置(从根源解决)
如果你的客户端用了缓存归一化(比如Apollo的InMemoryCache默认会根据id和__typename识别唯一数据),那得确保你的User类型有正确的唯一标识。因为你是用邮箱查询用户,说不定你的User类型里email才是唯一键,而不是默认的id?
那你可以在创建缓存的时候指定主键:
const cache = new InMemoryCache({ typePolicies: { User: { keyFields: ['email'] // 把邮箱设为User类型的唯一标识 } } });
这样当你更新用户数据后,客户端会自动匹配到对应的缓存条目,自动替换成新数据,根本不用手动操作。
4. 临时禁用缓存(应急排查)
如果上面的方法都暂时不想试,也可以在查询用户的时候设置fetchPolicy: 'network-only',让这个查询每次都直接从服务器拿最新数据,完全不走缓存:
const { data } = useQuery(GET_USER_BY_EMAIL, { variables: { email: cachedEmail }, fetchPolicy: 'network-only' });
不过这个方法不推荐长期用,毕竟缓存就是用来提升性能的,适合你临时排查问题的时候用。
5. 排查缓存污染
有时候可能是你在某个地方不小心写错了缓存,导致旧数据一直赖着不走。可以用客户端的调试工具(比如Apollo DevTools)查看缓存里的GetUserByEmail条目,看看里面的内容是不是旧的,有没有被其他操作意外覆盖。
内容的提问来源于stack exchange,提问作者sKyLineLOL

