Redux OfflineとApolloを使用したネットワーク接続なしのGraphQLクエリ

おもしろいですが、インターネットが世界に普及するにつれて、ネットワークに接続せずに動作するWebアプリケーションの需要が高まっています。 ユーザー(および顧客)は、オンライン、オフライン、および通信が不安定な地域で機能するインターネットアプリケーションを求めています。


そして、これは...簡単ではありません。


ReactおよびApollo Clientを使用したGraphQLデータレイヤーで、ネットワーク接続なしで機能する効果的なソリューションを作成する方法を見てみましょう。 この記事は2つの部分に分かれています。 今週はオフラインクエリを分析します。 来週、突然変異に取りかかりましょう。


Redux PersistとRedux Offline


Apollo Clientの背後には同じReduxがあります。 そしてそれは、多数のツールとライブラリを備えたReduxエコシステム全体がApolloアプリケーションで利用できることを意味します。


オフラインサポートでは、ReduxにはRedux PersistとRedux Offlineの2つの主要なプレーヤーがあります。


Redux Persistは、 reduxストレージをlocalStorageして復元するための優れた基本ツールです(他のストレージの場所がサポートされています)。 受け入れられている用語によると、回復は再水和とも呼ばれます。


Redux Offlineは、機能とユーティリティの追加レイヤーでRedux Persistの機能を拡張します。 Redux Offlineは、切断され復元された通信について自動的に学習します。中断すると、アクションと操作をキューに入れ、回復すると、これらのアクションを自動的に再現します。


Redux Offlineは、ネットワーク接続なしで作業するための「有料」オプションです。


オフラインリクエスト


Apollo Client自体は、ネットワーク接続の短期的な中断にうまく対処します。 要求が行われると、その結果はApolloの独自のリポジトリに配置されます。


同じリクエストが繰り返される場合、fetchPolicyパラメータがnetwork-onlyでない限り結果はクライアント上のリポジトリからすぐに取得され、リクエスト元のコンポーネントに返さます 。 これは、ネットワーク接続がない場合、またはサーバーが利用できない場合、リクエストを繰り返すと最後に保存された結果が返されることを意味します。


ただし、ユーザーがアプリケーションを閉じると、ストレージは消えます。 アプリケーションの再起動の間にクライアントのApolloストレージを失わないようにするには?


Redux Offlineが助けになります。


Apolloは、データをReduxリポジトリ( apolloキー)に保持します。 ストレージ全体をlocalStorage書き込み、次にアプリケーションを起動したときに復元することで、インターネットに接続していない場合でも、アプリケーションとのセッション間で過去のリクエストの結果を転送できます


Redux OfflineとApollo Clientの使用には微妙な違いがあります。 両方のライブラリを連携させる方法を見てみましょう。


手動ストレージ作成


通常、Apolloクライアントは非常に簡単に作成されます。


 export const client = new ApolloClient({ networkInterface }); 

ApolloClientコンストラクターは、Apolloリポジトリー(および間接的にReduxリポジトリー)を自動的に作成します。 結果のclientインスタンスはApolloProviderコンポーネントにApolloProviderます。


 ReactDOM.render( <ApolloProvider client={client}> <App /> </ApolloProvider>, document.getElementById('root') ); 

Redux Offlineを使用する場合、Reduxリポジトリを手動で作成する必要があります。 これにより、ストレージにRedux Offlineから中間プロセッサ(ミドルウェア)に接続できます。 開始するには、単にApolloが行うことを繰り返します。


 export const store = createStore( combineReducers({ apollo: client.reducer() }), undefined, applyMiddleware(client.middleware()) ); 

ここでは、 storeはApolloインスタンス( client変数)のリデューサーとミドルウェアを使用し、初期状態はundefinedです。


これで、 storeApolloProviderコンポーネントに送信できます。


 <ApolloProvider client={client} store={store}> <App /> </ApolloProvider> 

素晴らしい。 Reduxリポジトリの作成は制御下にあり、Reduxオフラインで続行できます。


永続クエリストレージの基本


最も単純な場合、Redux Offlineの追加は、別の中間ハンドラーをリポジトリに追加することです。


 import { offline } from 'redux-offline'; import config from 'redux-offline/lib/defaults'; export const store = createStore( ... compose( applyMiddleware(client.middleware()), offline(config) ) ); 

追加の構成を行わないと、 offlineハンドラーは自動的にReduxストレージのlocalStorageへの書き込みを開始しlocalStorage


信じられない?


コンソールを開き、 localStorageからこのエントリを取得しlocalStorage


 localStorage.getItem("reduxPersist:apollo"); 

Apolloアプリケーションの完全な現在の状態を表す大きなJSONオブジェクトが表示されます。



いいね!


Redux Offlineは、Reduxのストレージのスナップショットを自動的にlocalStorageし、 localStorage書き込みlocalStorage 。 アプリケーションが起動するたびに、保存された状態がlocalStorageから自動的に取得され、Reduxストレージに復元されます。


結果がこのリポジトリにあるすべてのクエリでは、サーバーへの接続がない場合でもデータが返されます。


ストレージリカバリコンペティション


残念ながら、ストレージの復旧はすぐには行われません。 Redux Offlineがストレージを復元している間にアプリケーションがリクエストを行うと、Strange Things(tm)が発生する場合があります。


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: …  21     .    , -         .       : … 

Redux Persistの開発者は問題を認識し、リカバリ後のアプリケーションの遅延レンダリングのレシピを提案しました。 残念ながら、彼のソリューションはpersistStore手動呼び出しに基づいています。 ただし、Redux Offlineはこの呼び出しを自動的に行います。


別のソリューションを見てみましょう。


REHYDRATE_STOREと呼ばれるReduxアクションと、Reduxリポジトリのrehydrated属性の値をtrueに設定する対応するリデューサーを作成します。


 export const REHYDRATE_STORE = 'REHYDRATE_STORE'; export default (state = false, action) => { switch (action.type) { case REHYDRATE_STORE: return true; default: return state; } }; 

作成されたレデューサーをリポジトリに接続し、リカバリの終了時に新しいアクションが実行されるようにReduxオフラインを構成します。


 export const store = createStore( combineReducers({ rehydrate: RehydrateReducer, apollo: client.reducer() }), ..., compose( ... offline({ ...config, persistCallback: () => { store.dispatch({ type: REHYDRATE_STORE }); }, persistOptions: { blacklist: ['rehydrate'] } }) ) ); 

いいね! Redux Offlineは、リポジトリを復元するときにpersistCallback関数を呼び出します。これにより、 REHYDRATE_STOREアクションがトリガーされ、最終的にリポジトリにrehydrateが設定されます。


blacklist配列にrehydrateを追加すると、ストレージのこの部分がlocalStorage書き込まれたり復元されたりすることがlocalStorage


リポジトリにはリカバリの完了に関する情報があるため、 rehydrateフィールドの変更に応答し、 rehydratetrue場合にのみ子コンポーネントを視覚化するコンポーネントを開発しtrue


 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); 

最後に、 <App />コンポーネントを<Rehydrated /> <App />コンポーネント内に配置して、水分補給が完了するまでアプリケーションが表示されないようにします。


 <ApolloProvider client={client} store={store}> <Rehydrated> <App /> </Rehydrated> </ApolloProvider> 

うわ


Redux OfflineがlocalStorageからストアを復元する間、アプリケーションはさりげなく待機し、レンダリングを続行し、その後のすべてのGraphQLクエリまたは変更を行います。


奇妙な説明


ApolloクライアントでRedux Offlineを使用する場合、いくつかの奇妙な点と明確なことがあります。


まず、この記事の例ではapollo-clientパッケージのバージョン1.9.0-0を使用していることに注意してください。 Apollo Client バージョン1.9では、 Redux Offlineを使用する際の奇妙な症状の修正発表されました。


このペアのもう1つの奇妙な点は、Redux OfflineがApollo Client Devtoolsとうまく連携していないように見えることです。 インストールされたDevtoolsでRedux Offlineを使用しようとすると、予期せず一見矛盾したエラーが発生する場合があります。


このようなエラーは、Apollo client作成時にDevtoolsに接続しなければ簡単に解消できclient


 export const client = new ApolloClient({ networkInterface, connectToDevTools: false }); 

連絡を取り合う


Redux Offlineは、Apolloアプリケーションに、サーバーから切断した後にアプリケーションが再起動した場合でも、リクエストを実行するための基本的なメカニズムを提供します。


1週間後、Redux Offlineを使用したオフライン突然変異の処理に飛び込みます。


連絡を取り合いましょう!


記事の翻訳。 ピート・コーリーによるオリジナル。



Source: https://habr.com/ru/post/J335210/


All Articles