3

21 分钟学 apollo-client 是一个系列,简单暴力,包学包会。

搭建 Apollo client 端,集成 redux
使用 apollo-client 来获取数据
修改本地的 apollo store 数据
提供定制方案

写入 store 的失败原因分析和解决方案

大坑 - 写入数据失败

同志们注意啦!整个 apollo 的坑都集中在这里啦!

一旦返回的数据写入 apollo store 失败,会 静默失败 ,最终你发现怎么数据没有发生任何变化? (坑爹啊!)

一个简单的办法是,你去 node_modules 下,把报错信息暴露出来。是的,经过阅读源码,我发现这已经是最简单的办法了...

具体代码在 node_modules\apollo-client\data\writeToStore.js

add_log2.png
add_log.png

如果这解决了你的问题,请给我打赏,谢谢。

写入失败的原因

那什么时候会写入失败呢?

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,
    user: {
        ...prev.user,
        statistic: {
            upvoted: false,
+            __typename: 'UserStatistic',
        }
    }
}

总结

如果没有报错消息的话,上面这种情况是极其难以排查的,尤其是我们特别容易遇到缺少 __typename 的场景。

为了减少此类事故的发生,你可以

  • 和后端协商,不要返回 null 值
  • 不论何时何地,何种层级,永远给数据手动添加 __typename
  • 不用 apollo









tinkgu
503 声望20 粉丝

{{user.signature}}