- 原文地址: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、前端、后端、区块链、产品、设计、人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。