21 分鐘學 apollo-client 系列:寫入失敗的緣由和解決方案

21 分鐘學 apollo-client 是一個系列,簡單暴力,包學包會。javascript

搭建 Apollo client 端,集成 redux
使用 apollo-client 來獲取數據
修改本地的 apollo store 數據
提供定製方案java

寫入 store 的失敗緣由分析和解決方案node

大坑 - 寫入數據失敗

同志們注意啦!整個 apollo 的坑都集中在這裏啦!redux

一旦返回的數據寫入 apollo store 失敗,會 靜默失敗 ,最終你發現怎麼數據沒有發生任何變化? (坑爹啊!)segmentfault

一個簡單的辦法是,你去 node_modules 下,把報錯信息暴露出來。是的,通過閱讀源碼,我發現這已是最簡單的辦法了...後端

具體代碼在 node_modules\apollo-client\data\writeToStore.jsapi

add_log2.png
add_log.png

若是這解決了你的問題,請給我打賞,謝謝。fetch

寫入失敗的緣由

那何時會寫入失敗呢?ui

1. 寫入的數據,其結構不符合 query schema
這意味着你不能基於 query schema 添加一些你本地須要的數據,好比 isSelected 什麼的spa

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
相關文章
相關標籤/搜索