21 分钟学 apollo-client 是一个系列,简单暴力,包学包会。
搭建 Apollo client 端,集成 redux
使用 apollo-client 来获取数据
修改本地的 apollo store 数据
提供定制方案
大坑 - 写入数据失败
同志们注意啦!整个 apollo 的坑都集中在这里啦!
一旦返回的数据写入 apollo store 失败,会 静默失败 ,最终你发现怎么数据没有发生任何变化? (坑爹啊!)
一个简单的办法是,你去 node_modules 下,把报错信息暴露出来。是的,经过阅读源码,我发现这已经是最简单的办法了...
具体代码在 node_modules\apollo-client\data\writeToStore.js
如果这解决了你的问题,请给我打赏,谢谢。
写入失败的原因
那什么时候会写入失败呢?
1. 写入的数据,其结构不符合 query schema
这意味着你不能基于 query schema 添加一些你本地需要的数据,比如 isSelected 什么的
2. 丢失 __typename
信息
不能丢失任何层级上的 __typename
!
当你 log fetchMoreResult
你会发现,它在很多层级上附带了 __typename
,这个数据用于检查写入数据是否匹配 query schema。
一旦你丢失了 __typename
,可能会导致写入失败,或者尽管写入了,但本该携带 __typename
的那一层的数据没有写入。
你可能说,那我当心一点,总是使用 Object.assign
或者 { ...obj,a: 1 }
这种 spread 赋值的方式吧。
年轻人,你还是太天真啊!
如果你得到一组数据,嵌套的某一个值为 null
,那么它本来就不携带 __typename
!
如果这个值为 null
的数据在你的 query schema 中确实需要 __typename
,那即使你对这个数据重新赋值,也无法被写入到 apollo store 中,因为缺少 __typename
信息。
上面这段话比较绕,给你看个例子。
假设你有这样一个 query
query { user { # typename 为 User id nickname statistic { # typename 为 UserStatistic upvoted } } }
然后,得到一组数据
const prev = { user: { __typename: 'User',id: 1,nickname: 'apollo 真是坑爹啊',statistic: null } }
为了 ui 显示正常,你会给 statistic 设置一些默认值
const next = { ...prev,user: { ...prev.user,statistic: { upvoted: false,} } }
如果你将 next 数据 writeToStore 你就会就发现 statistic 还是为 null
,这些数据还是没有被写入 apollo store。
这是因为,user.statistic
在 schema 中定义的 type 是 UserStatistic
,而原始数据由于为 null
,所以本该自动附加的 __typename
属性也不存在。这导致你在写入 store 的时候,缺少了必要的 __typename
信息,apollo 将拒绝接受。
很扯吧。
要让上面的代码 work 起来,你需要
const next = { ...prev,+ __typename: 'UserStatistic',} } }
总结
如果没有报错消息的话,上面这种情况是极其难以排查的,尤其是我们特别容易遇到缺少 __typename
的场景。
为了减少此类事故的发生,你可以
- 和后端协商,不要返回 null 值
- 不论何时何地,何种层级,永远给数据手动添加
__typename
- 不用 apollo