React Native中使用Apollo-React调用GraphQL Union报错求助
解决React Native + Apollo GraphQL处理Union类型的"Requested keys of a value that is not an object"错误
我之前也碰到过几乎一模一样的问题!结合你给出的场景和代码,这个错误大概率是React Native环境下Apollo Cache处理Union类型时的序列化/反序列化差异导致的——毕竟关闭远程调试时RN用的是Hermes或JSC引擎,和Chrome的V8(远程调试依赖的引擎)行为不一样,再加上服务端的__typename返回可能有遗漏,就触发了这个报错。
下面是我亲测有效的解决方案,按优先级尝试:
1. 先确认服务端返回的__typename字段是否存在
这是最常见的根源!Apollo识别Union类型完全依赖返回数据里的__typename字段,而有些服务端框架(比如你用的Youshido bundle)默认不会自动添加这个字段。
- 关闭远程调试,用Charles抓包或者RN的Network Inspector查看实际返回的JSON,比如
questions数组里的每个元素是否带有__typename:"questions": [ { "__typename": "QuestionUnionTypeA", "id": "1", "parameterA": "test" } ] - 如果没有这个字段,需要在服务端配置开启自动添加
__typename,或者在你的GraphQL查询里手动请求它(虽然Apollo应该会自动注入,但保险起见可以加上):const FindForm = gql`query FindForm($id: ID!){ findForm(id: $id){ id title questions { id __typename # 手动添加 ... on QuestionUnionTypeA { parameterA } ... on QuestionUnionTypeB { parameterB } ... on QuestionUnionTypeC { parameterC } } } }`;
2. 用Apollo CLI自动生成Fragment Matcher配置
你手动写的Fragment Matcher可能存在拼写错误或者遗漏,用官方工具生成更可靠:
- 安装Apollo CLI:
npm install -g apollo - 下载服务端的Schema:
apollo client:download-schema --endpoint=http://10.1.68.64:8000/graphql schema.json - 生成Fragment Matcher的配置文件:
apollo client:codegen --localSchemaFile=schema.json --target=javascript --outputFlat src/fragmentTypes.js - 替换你手动写的Fragment Matcher:
import { IntrospectionFragmentMatcher } from 'apollo-cache-inmemory'; import introspectionResult from './fragmentTypes'; const fragmentMatcher = new IntrospectionFragmentMatcher({ introspectionQueryResultData: introspectionResult });
3. 调整InMemoryCache的ID生成逻辑
React Native的引擎在处理缓存对象时,对无唯一标识的Union类型可能会有序列化问题,添加dataIdFromObject确保每个类型都有唯一ID:
import { InMemoryCache, defaultDataIdFromObject } from 'apollo-cache-inmemory'; const cache = new InMemoryCache({ fragmentMatcher, dataIdFromObject: (obj) => { // 给每个Union类型实例生成唯一ID switch(obj.__typename) { case 'QuestionUnionTypeA': return `QuestionA:${obj.id}`; case 'QuestionUnionTypeB': return `QuestionB:${obj.id}`; case 'QuestionUnionTypeC': return `QuestionC:${obj.id}`; default: return defaultDataIdFromObject(obj); // 其他类型用默认逻辑 } } });
4. 临时跳过缓存验证问题
如果上面的方法还没解决,试试在查询里禁用缓存,确认是不是缓存处理的问题:
const query = graphql(FindForm, { options: { variables: { id: this.state.id }, fetchPolicy: 'network-only' // 直接请求网络,不使用缓存 } })(resultDisplayer);
如果这样不报错,就可以确定是缓存配置的问题,回到前面的步骤调整。
为什么远程调试时正常?
远程调试时,RN的JS代码跑在Chrome的V8引擎里,而本地运行时用的是Hermes或JSC,这两个引擎在JSON序列化、对象处理上有细微差异——V8可能对缺失__typename或者缓存对象的不规范结构更宽容,而Hermes/JSC会严格校验,从而抛出错误。
内容的提问来源于stack exchange,提问作者R.Duteil
相关产品推荐
相关产品推荐

