- 原文地址:Offline GraphQL Queries with Redux Offline and Apollo
- 原文做者:Pete Corey
- 譯文出自:掘金翻譯計劃
- 本文永久連接:github.com/xitu/gold-m…
- 譯者:vitoxli
- 校對者:Baddyo
具備諷刺意味的是,在咱們日益鏈接的世界中,對 web 應用程序離線功能的需求正在不斷增加。咱們的用戶(和客戶端)但願在聯機、脫機和在鏈接不穩定的區域使用富互聯網應用。javascript
這實際上是一件很困難的事情。html
讓咱們深刻探討如何經過 React 和 Apollo Client 提供的 GraphQL data layer 構建一個功能強大的脫機解決方案。這篇文章將會分爲兩部分,本週咱們將會討論脫機查詢。下週咱們將會討論脫機修改。前端
在底層,Apollo Client 由 Redux 提供支持。這意味着咱們能夠在 Apollo 應用程序中使用整個 Redux 生態系統中的工具和庫。java
在 Redux 離線支持的世界中,有兩個主要參與者:Redux Persist 和 Redux Offline。react
Redux Persist 是一個很是棒但很簡單的工具。它被設計用來從 localStorage
(或者從這些支持的存儲引擎)中存儲和檢索(或者說「rehydrate」) redux store。android
Redux Offline 擴展自 Redux Persist 並添加功能和實用層。Redux Offline 自動檢測網絡的斷開和從新鏈接,容許您在脫機時將操做排入隊列,並在從新鏈接後自動重試這些操做。ios
Redux Offline 是離線支持的標配選項。🔋git
開箱即用地,Apollo Client 在部分鏈接的網絡狀況下工做得至關好。客戶一旦進行查詢,該查詢的結果就會保存到 Apollo store。github
若是使用 network only
之外的任何 fetchPolicy
再次執行同一查詢,則該查詢的結果將當即從客戶端的 store 中提取出來並返回到查詢組件。這意味着,即便咱們的客戶端與服務器斷開鏈接,重複的查詢仍將返回最新的可用結果。web
不幸的是,一旦用戶關閉咱們的應用,他們的 store 就會丟失。那如何在應用重啓的狀況下來持久化客戶機的 Apollo store 呢?
Redux Offline 正是解決問題的良藥!
Apollo store 實際上存在於咱們的應用的 Redux store(在 apollo
key 中)中。經過將整個 Redux store 持久化到 localStorage
中,並在每次加載應用程序時從新獲取。經過這種方法,即使在斷開網絡鏈接時,咱們也能夠經過應用程序從新啓動來傳遞過去查詢的結果!
在 Apollo Client 應用程序中使用 Redux Offline 並不是不存在問題。讓咱們看看如何讓這兩個庫協同工做。
一般狀況下,創建一個 Apollo client 十分簡單:
export const client = new ApolloClient({
networkInterface
});
複製代碼
ApolloClient
的構造函數將自動爲咱們建立 Apollo store(並間接建立咱們的 Redux store)。咱們只需將這個新的 client
放入咱們的 ApolloProvider
組件中:
ReactDOM.render(
<ApolloProvider client={client}> <App /> </ApolloProvider>,
document.getElementById('root')
);
複製代碼
當使用 Redux Offline 時,咱們須要手動構造 Redux store,以傳入 Redux Offline 的中間件。首先,讓咱們來重現 Apollo 爲咱們所作的一切:
export const store = createStore(
combineReducers({ apollo: client.reducer() }),
undefined,
applyMiddleware(client.middleware())
);
複製代碼
新的 store
使用了 Apollo client
爲咱們提供的 reducer 和 middleware,並使用了一個值爲 undefined
的初始 store 來進行初始化。
咱們如今能夠把這個 store
傳入咱們的 ApolloProvider
中:
<ApolloProvider client={client} store={store}>
<App /> </ApolloProvider>
複製代碼
完美。既然咱們已經手動建立了 Redux store,咱們就可使用 Redux Offline 來開發支持離線的應用。
以最簡單的形式引入 Redux Offline,包括爲咱們的 store 添加一箇中間件:
import { offline } from 'redux-offline';
import config from 'redux-offline/lib/defaults';
複製代碼
export const store = createStore(
...
compose(
applyMiddleware(client.middleware()),
offline(config)
)
);
複製代碼
這個 offline
中間件將會自動地把咱們的 Redux store 持久化到 localStorage
中。
不相信我嗎?
啓動你的控制檯並查看此 localStorage
:
localStorage.getItem("reduxPersist:apollo");
複製代碼
你將會看到一個巨大的 JSON blob,它表明着你 Apollo 應用程序的整個當前狀態。
太棒啦!
Redux Offline 如今將自動地把 Redux store 的快照保存到 localStorage
中。任什麼時候候從新加載應用程序,此狀態都將自動從 localStorage
中提取並 rehydrate 到你的 Redux store 中。
即便當前應用程序已與服務器斷開鏈接,任何在 store 中使用此方案的查詢都將返回該數據。
不幸地是,store 的 rehydration 不是即刻完成的。若是咱們的應用程序試圖在 Redux Offline 取得 store 時進行查詢,奇怪的事情就會發生啦。
若是咱們打開了 Redux Offline 的 autoRehydrate
日誌記錄(這自己就是一種折磨),咱們會在首次加載應用程序時會看到相似的錯誤:
21 actions were fired before rehydration completed. This can be a symptom of a race condition where the rehydrate action may overwrite the previously affected state. Consider running these actions after rehydration: …
Redux Persist 的做者認可了這一點,並已經編寫了一種延遲應用程序的渲染直到完成 rehydration 的方法。不幸的是,他的解決方案依賴於手動調用 persistStore
,而 Redux Offline 已經默默爲咱們作了這項工做。
讓咱們看看其它的解決方法。
咱們將會建立一個新的 Redux action,並將其命名爲 REHYDRATE_STORE
,同時咱們建立一個對應的 reducer,並在咱們的 Redux store 中設置一個值爲 true
的 rehydrated
標誌位:
export const REHYDRATE_STORE = 'REHYDRATE_STORE';
複製代碼
export default (state = false, action) => {
switch (action.type) {
case REHYDRATE_STORE:
return true;
default:
return state;
}
};
複製代碼
如今讓咱們把這個新的 reducer 添加到咱們的 store 中,而且告訴 Redux Offline 在獲取到 store 的時候觸發咱們的 action:
export const store = createStore(
combineReducers({
rehydrate: RehydrateReducer,
apollo: client.reducer()
}),
...,
compose(
...
offline({
...config,
persistCallback: () => {
store.dispatch({ type: REHYDRATE_STORE });
},
persistOptions: {
blacklist: ['rehydrate']
}
})
)
);
複製代碼
完美。當 Redux Offline 恢復完咱們的 store 後,會觸發 persistCallback
回調函數,這個函數會 dispatch 咱們的 REHYDRATE_STORE
action,並最終更新咱們 store 中的 rehydrate
。
將 rehydrate
添加到 Redux Offline 的黑名單
能夠確保咱們的 store 永遠不會存儲到 localStorage
或從 localStorage
取得咱們的 store。
既然咱們的 store 能準確地反映是否發生了 rehydration 操做,那麼讓咱們編寫一個組件來監聽 rehydrate
字段,而且只在 rehydrate
爲 true
時對它的 children 進行渲染。
class Rehydrated extends Component {
render() {
return (
<div className="rehydrated"> {this.props.rehydrated ? this.props.children : <Loader />} </div>
);
}
}
export default connect(state => {
return {
rehydrate: state.rehydrate
};
})(Rehydrate);
複製代碼
最後,咱們能夠用新的 <Rehydrate>
組件把 <App/>
組件包裹起來,以防止應用程序在 rehydration 以前進行渲染:
<ApolloProvider client={client} store={store}>
<Rehydrated>
<App />
</Rehydrated>
</ApolloProvider>
複製代碼
哇哦。
如今,咱們的應用程序能夠愉快地等待 Redux Offline 從 localStorage
中徹底取得咱們的 store,而後繼續渲染並進行任何後續的 GraphQL 查詢或修改了。
在配合 Apollo client 使用 Redux Offline 時,須要注意如下這些事項。
首先,須要注意的是本文的示例使用的是 1.9.0-0
版本的 apollo-client
包。Apollo Client 在 1.9 版本中引入了修復程序,來解決與 Redux Offline 同時使用時的一些怪異表現。
與此文相關的另外一個須要關注的點是,Apollo Clinent Devtools 對 Redux Offline 的支持不太友好。在安裝了 Devtools 的狀況下使用 Redux Offline 有時會致使意外的錯誤。
在建立 Apollo client
實例時,不鏈接 Devtools 便可很容易避免這些錯誤:
export const client = new ApolloClient({
networkInterface,
connectToDevTools: false
});
複製代碼
Redux Offline 應該爲您的 Apollo 支持的 React 應用程序的查詢解析提供基本支持,即便您的應用程序是在與服務器斷開鏈接時從新加載的。
下週咱們會進一步探討如何使用 Redux Offline 處理脫機修改的問題。
敬請期待!
若是發現譯文存在錯誤或其餘須要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可得到相應獎勵積分。文章開頭的 本文永久連接 即爲本文在 GitHub 上的 MarkDown 連接。
掘金翻譯計劃 是一個翻譯優質互聯網技術文章的社區,文章來源爲 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智能等領域,想要查看更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。