React/Apollo应用数据异常:Network数据正确但Apollo返回错误
React/Apollo 应用缓存导致数据混乱问题解决
我开发的React/Apollo应用出现数据异常:Chrome网络面板显示接口原始返回数据正确,但Apollo返回的数据却出现错误。
预期与实际数据对比
预期返回格式
{ id: 12345, name: 'some record', status: { id: 1, name: 'Active' }, types: [ { id: 1, name: 'Client' }, { id: 4, name: 'Example' } ], // ... 更多属性 }
实际Apollo返回格式
{ id: 12345, name: 'some record', status: { id: 1, name: 'Client' }, // ← 此处"Active"被错误替换为"Client" types: [ { id: 1, name: 'Client' }, { id: 4, name: 'Example' } ], // ... 更多属性 }
问题原因
Apollo缓存默认使用id字段作为实体的唯一标识,当不同类型的实体(如Status和Type)ID相同时,缓存会错误地将它们视为同一个实体,导致数据覆盖。
解决办法
1. 单个查询禁用缓存(临时方案)
在useQuery的配置中添加fetchPolicy: 'no-cache',让该查询跳过缓存,直接使用网络返回的数据:
const { data, refetch } = useQuery(gql`...`, { variables: {...}, fetchPolicy: 'no-cache' });
也可以使用fetchPolicy: 'network-only',强制每次从网络获取数据,但仍会将结果写入缓存。
2. 为实体添加唯一缓存键(推荐方案)
通过配置Apollo Client的dataIdFromObject,使用__typename + id作为缓存的唯一标识,区分不同类型的实体:
import { ApolloClient, InMemoryCache } from '@apollo/client'; const client = new ApolloClient({ uri: '/graphql', cache: new InMemoryCache({ dataIdFromObject: (object) => { if (object.__typename && object.id) { // 用类型名+ID生成唯一缓存键 return `${object.__typename}:${object.id}`; } return null; // 其他情况由Apollo自动处理 } }) });
大部分GraphQL服务会自动为返回的实体添加__typename字段,确保不同类型的同ID实体不会被缓存混淆。
3. 全局禁用缓存(不推荐)
如果完全不需要缓存功能,可以全局配置Apollo Client禁用缓存,但这会影响应用性能,仅作为极端情况使用:
const client = new ApolloClient({ uri: '/graphql', cache: new InMemoryCache({ addTypename: false }), defaultOptions: { query: { fetchPolicy: 'no-cache', }, }, });
我的查询代码
const { data, refetch } = useQuery(gql` query GetScoreAudit( $page: Int, $pageSize: Int, $search: String, $orderBy: String, $order: Order, $status: Int, $type: Int ) { scoreAudit( page: $page, pageSize: $pageSize, search: $search, orderBy: $orderBy, order: $order, status: $status, type: $type ) { total nodes { id name managedWorkstations managedServers firewall wpsCompliance maxWpsCompliance mrr tpe status { id name } types { id name } serviceCompanies { service exists } } } } `, { variables: { page: tableOpts.pagination.page, pageSize: tableOpts.pagination.pageSize, search: tableOpts.filters.search, orderBy: tableOpts.orderBy, order: tableOpts.order, status: tableOpts.filters.status, type: tableOpts.filters.type } }); console.log(data);
内容的提问来源于stack exchange,提问作者chrispytoes
相关产品推荐
相关产品推荐

