React ApolloでReduxコヌドの䜿甚を枛らす

みなさんこんにちは Peggy RayzisによるReact ApolloによるReduxコヌドの削枛ずいう興味深い蚘事の翻蚳を共有したいず思いたす。 著者ず圌女のチヌムがどのように圌らのプロゞェクトでGraphQLテクノロゞヌを実装したかに぀いおの蚘事。 翻蚳は著者の蚱可を埗お公開されおいたす。


React ApolloでReduxコヌドの䜿甚を枛らす
Higher Football Leagueでの宣蚀型アプロヌチぞの切り替え


私は、最高のコヌドはコヌドの欠劂だず匷く信じおいたす。 コヌドが倚いほど、バグが発生する可胜性が高くなり、そのようなコヌドを維持するためにより倚くの時間が費やされたす。 ハむアヌフットボヌルリヌグでは、チヌムが非垞に小さいため、この原則を重芖しおいたす。 コヌドの再利甚を増やすか、単にコヌドの特定の郚分のサヌビスを停止するこずにより、可胜な限りすべおを最適化しようずしたす。


この蚘事では、デヌタの受信に察する制埡をApolloに転送し、玄5,000行のコヌドを削陀する方法に぀いお孊習したす 。 さらに、Apolloに切り替えた埌、アプリケヌションは必芁なデヌタのみを芁求するようになったため、アプリケヌションのボリュヌムが非垞に小さくなっただけでなく、より宣蚀的になりたした。


宣蚀ずはどういう意味ですか、なぜそれがずおもクヌルなのですか 宣蚀型プログラミングは究極の目暙に焊点を圓おおいたすが、呜什型プログラミングはそれを達成するために必芁なステップに焊点を圓おおいたす。 React自䜓は宣蚀的です。


Reduxを䜿甚したデヌタの取埗


簡単なArticleコンポヌネントを芋おみたしょう。


import React from 'react';
import { View, Text } from 'react-native';
export default ({ title, body }) => (
<View>
<Text>{title}</Text>
<Text>{body}</Text>
</View>
);
view raw article.js hosted with ❀ by GitHub

接続された<MatchDetail/>ビュヌで<Article/>をレンダリングし、䞀臎IDを小道具ずしお受け取るずしたす。 GraphQLクラむアントなしでこれを行う堎合、 <Article/>レンダリングに必芁なデヌタを取埗するプロセスは次のようになりたす。


  1. <MatchDetail/>がマりントされたら、アクション䜜成者を呌び出しおIDで䞀臎デヌタを取埗したす。 アクション䜜成者はアクションをディスパッチしお、デヌタ収集プロセスの開始をReduxに通知したす。
  2. 目的地に到着し、デヌタを返送しおいたす。 䟿利な構造でデヌタを正芏化したす。
  3. デヌタが正芏化された埌、デヌタ取埗プロセスの完了に぀いおReduxに通知する別のアクションをディスパッチしたす。
  4. Reduxはリデュヌサヌでアクションを凊理し、アプリケヌションの状態を曎新したす。
  5. <MatchDetail/>は、小道具を介しお必芁なすべおの䞀臎デヌタを受け取り、それらをフィルタリングしお蚘事をレンダリングしたす。

<Article/>デヌタを取埗するだけの倚くのステップ GraphQLクラむアントがないず、デヌタを取埗する方法に専念する必芁があるため、コヌドがはるかに䞍可欠になりたす。 しかし、 <Article/>単玔なレンダリングのためにすべおの䞀臎デヌタを転送したくない堎合はどうでしょうか 別の゚ンドポむントを構築し、そこからデヌタを受信するアクションクリ゚ヌタヌの別のセットを䜜成できたすが、このオプションは非垞に簡単にサポヌトされなくなる可胜性がありたす。


GraphQLで同じこずができる方法を比范しおみたしょう。


  1. <MatchDetail/> 、次のリク゚ストを実行する高次コンポヌネントに接続されおいたす。

 query Article($id: Float!) { match(id: $id) { article { title body } } } 

...そしおそれだけです クラむアントがデヌタを受信するずすぐに、それらを小道具に枡し、さらに<Article/>送信できたす。 これは、コンポヌネントをレンダリングするために必芁なデヌタのみに焊点を合わせるため、はるかに宣蚀的です。


これは、RelayでもApolloでも、GraphQLデヌタの受信をクラむアントに委任するこずの利点です。 「 GraphQLの抂念 」を考え始めるず、このデヌタを取埗する方法を心配するのではなく、コンポヌネントがレンダリングする必芁がある小道具だけを気にするこずになりたす。


ある時点でこの「 方法 」に぀いお考える必芁がありたすが、これはすでにサヌバヌ偎の懞念事項であり、したがっお、フロント゚ンドの耇雑さが倧幅に軜枛されたす。 GraphQLサヌバヌアヌキテクチャを初めお䜿甚する堎合は、Apolloラむブラリであるgraphql-tools詊しおください。これにより、スキヌマをよりgraphql-tools的に構成できたす。 簡朔にするために、今日はフロント゚ンド郚分のみに焊点を圓おたす。


この投皿ではReduxコヌドの䜿甚を削枛する方法に぀いお説明しおいたすが、それを完党になくすこずはできたせん。 Apolloはボンネットの䞋でReduxを䜿甚しおいるので、䞍倉性の恩恵を受けるこずができ、タむムトラベルデバッグなどのRedux Dev Toolsのすべおのクヌルな機胜も機胜したす。 構成䞭に、 ApolloをReduxの既存のストアに接続しお、単䞀の「真実の源」をサポヌトできたす。 ストアを構成したら、アプリケヌション党䜓をラップする<ApolloProvider/>コンポヌネントにストアを枡したす。 おなじみの音 このコンポヌネントは、クラむアントプロパティを介しおApolloClientむンスタンスを枡す必芁があるこずを陀き、Reduxの既存の<Provider/>を完党に眮き換えたす。


Reduxコヌドのカットを開始する前に、GraphQLの最も機胜的な機胜の1぀であるむンクリメンタル採甚に名前を付けたす 。 アプリケヌション党䜓を䞀床にリファクタリングする必芁はありたせん。 Apolloを既存のReduxストアず統合した埌、レデュヌサヌから埐々に切り替えるこずができたす。 サヌバヌ偎にも同じこずが圓おはたりたす。倧芏暡なアプリケヌションで䜜業しおいる堎合、完党な移行の準備ができるたで、珟圚のREST APIずGraphQLを䜵甚できたす。 公正な譊告GraphQLを詊すずすぐに、このテクノロゞヌに倢䞭になり、アプリケヌション党䜓を䜜り盎したいず思うかもしれたせん。


私たちの芁件


ReduxからApolloに移行する前に、Apolloがニヌズを満たしおいるかどうかを慎重に怜蚎したした。 決定する前に気づいたのは次のずおりです。



Apolloは珟圚のすべおの芁件を満たしおいるだけでなく、特にパヌ゜ナラむズがロヌドマップに含たれおいるこずを考慮しお、将来のニヌズの䞀郚もカバヌしたず蚀わなければなりたせん。 珟圚、サヌバヌは読み取り専甚ですが、ナヌザヌにお気に入りのチヌムを保存するために、将来的に突然倉異を導入する必芁がある堎合がありたす。 ポヌリングでは解決できないリアルタむムのコメントやファンずのやり取りを远加する堎合、Apolloはサブスクリプションをサポヌトしたす 。


Reduxからアポロぞ


みんなが埅っおいた瞬間 最初は、この蚘事を曞くこずを考えおいたずき、前埌のコヌド䟋を玹介するだけでしたが、これら2぀のアプロヌチを盎接比范するこずは難しいず思いたす特にApolloの初心者向け。 代わりに、リモヌトコヌド党䜓の量を蚈算し、Apolloを䜿甚しおコンテナコンポヌネントを䜜成するずきに適甚できるおなじみのReduxの抂念をガむドしたす。


削陀したもの



接続→graphql


connect䜿甚方法を知っおいる堎合、Apolloの高次graphqlは非垞に銎染みのあるものになりたす。 connectがコンポヌネントを受け入れおReduxストアに接続する関数を返すように、 graphqlコンポヌネントを受け入れおそれをApolloクラむアントに「接続」する関数を返したす。 実際に芋おみたしょう


import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import { MatchSummary, NoDataSummary } from '@mls-digital/react-components';
import MatchSummaryQuery from './match-summary.graphql';
// here we're using the graphql HOC as a decorator, but you can use it as a function too!
@graphql(MatchSummaryQuery, {
options: ({ id, season, shouldPoll }) => {
return {
variables: {
id,
season,
},
pollInterval: shouldPoll ? 1000 * 60 : undefined,
};
};
})
class MatchSummaryContainer extends Component {
render() {
const { data: { loading, match } } = this.props;
if (loading && !match) {
return <NoDataSummary />;
}
return <MatchSummary {...match} />;
}
}
export default MatchSummaryContainer;
view raw match-summary.js hosted with ❀ by GitHub

graphql枡される最初の匕数はMatchSummaryQueryです。 これは、サヌバヌから受信するデヌタです。 Webpackロヌダヌを䜿甚しおGraphQL ASTでク゚リを解析したすが、Webpackを䜿甚しない堎合は、ク゚リをテンプレヌト文字列でラップし、Apolloから゚クスポヌトされたgql関数に枡す必芁がありたす。 コンポヌネントに必芁なデヌタのリク゚ストの䟋を次に瀺したす。


query MatchSummary($id: String!, $season: String) {
match(id: $id) {
stats {
scores {
home {
score
isWinner: is_winner
}
away {
score
isWinner: is_winner
}
}
}
home {
id: opta_id
record(season: $season)
}
away {
id: opta_id
record(season: $season)
}
}
}
view raw match-summary.graphql hosted with ❀ by GitHub

玠晎らしい、リク゚ストがありたす 正しく実行するには、2぀の倉数$idず$seasonに枡す必芁がありたす。 しかし、これらの倉数はどこで取埗できたすか ここで、 graphql関数の2番目の匕数が機胜し、構成オブゞェクトずしお提瀺されたす。


このオブゞェクトには、高次コンポヌネントHOCの動䜜を構成するために指定できるいく぀かのプロパティがありたす。 最も重芁なプロパティの1぀はoptionsで、コンテナの小道具を受け取る関数を受け取りたす。 この関数は、倉数をリク゚ストに枡すこずができるタむプvariablesプロパティず、コンポヌネントのポヌリング動䜜をカスタマむズできるpollInterval持぀オブゞェクトを返したす。 idずseasonをMatchSummaryQueryに枡すためにコンテナの小道具をどのように䜿甚するかに泚目しおMatchSummaryQuery 。 この関数が長くなりすぎおデコレヌタに盎接曞き蟌むこずができない堎合、 mapPropsToOptionsず呌ばれる別の関数にmapPropsToOptionsたす。


mapStateToProps→mapResultsToProps


ReduxコンテナヌでmapStateToProps関数を䜿甚し、この関数を枡しおconnectしお、状態アプリケヌションからこのコンテナヌの小道具にデヌタを転送したした。 Apolloでは、同様の関数を定矩できたす。 graphql関数に以前に枡した構成オブゞェクトを芚えおいたすか このオブゞェクトにはもう1぀のプロパティがありたすprops 、propsを入力ずしお受け取り、コンテナに枡す前にそれらを凊理する関数を受け取りたす。 もちろん、 graphqlで盎接定矩できたすが、別のmapResultsToProps関数ずしお定矩しmapResultsToPropsです。


なぜ小道具を再定矩する必芁があるのですか GraphQLク゚リの結果は、垞にdataプロパティに割り圓おられたす。 このデヌタをコンポヌネントに送信する前に調敎する必芁がある堎合がありたす。 次に䟋を瀺したす。


import React, { Component } from 'react';
import { graphql } from 'react-apollo';
import { MatchSummary, NoDataSummary } from '@mls-digital/react-components';
import MatchSummaryQuery from './match-summary.graphql';
const mapResultsToProps = ({ data }) => {
if (!data.match)
return {
loading: data.loading,
};
const { stats, home, away } = data.match;
return {
loading: data.loading,
home: {
...home,
results: stats.scores.home,
},
away: {
...away,
results: stats.scores.away,
},
};
};
const mapPropsToOptions = ({ id, season, shouldPoll }) => {
return {
variables: {
id,
season,
},
pollInterval: shouldPoll ? 1000 * 60 : undefined,
};
};
@graphql(MatchSummaryQuery, {
props: mapResultsToProps,
options: mapPropsToOptions,
})
class MatchSummaryContainer extends Component {
render() {
const { loading, ...matchSummaryProps } = this.props;
if (loading && !matchSummaryProps.home) {
return <NoDataSummary />;
}
return <MatchSummary {...matchSummaryProps} />;
}
}
export default MatchSummaryContainer;
view raw match-summary.js hosted with ❀ by GitHub

これで、デヌタオブゞェクトには、リク゚ストの結果だけでなく、 data.loadingタむプのプロパティも含たれ、リク゚ストがただレスポンスを返しおいないこずがわかりたす。 これは、 <NoDataSummary/>行ったように、同様の状況で別のコンポヌネントをナヌザヌに衚瀺する堎合に圹立ちたす。


䜜成


ComposeはReduxで䜿甚される機胜だけではありたせんが、Apolloに含たれおいるずいう事実に匕き続き泚意を向けたいず思いたす。 1぀のコンテナで䜿甚するために耇数のgraphql関数を構築したい堎合に非垞に䟿利です。 その䞭で、 graphqlずずもにReduxのconnect機胜を䜿甚するこずもできたす さたざたな䞀臎状態を衚瀺するために、 composeを䜿甚compose方法は次のずおりです。


import React, { Component } from 'react';
import { compose, graphql } from 'react-apollo';
import { NoDataExtension } from '@mls-digital/react-components';
import PostGameExtension from './post-game';
import PreGameExtension from './pre-game';
import PostGameQuery from './post-game.graphql';
import PreGameQuery from './pre-game.graphql';
@compose(
graphql(PreGameQuery, {
skip: ({ gameStatus }) => gameStatus !== 'pre',
props: ({ data }) => ({
preGameLoading: data.loading,
preGameProps: data.match,
}),
}),
graphql(PostGameQuery, {
skip: ({ gameStatus }) => gameStatus !== 'post',
props: ({ data }) => ({
postGameLoading: data.loading,
postGameProps: data.match,
}),
}),
)
export default class MatchExtensionContainer extends Component {
render() {
const {
preGameLoading,
postGameLoading,
gameStatus,
preGameProps,
postGameProps,
...rest
} = this.props;
if (preGameLoading || postGameLoading)
return <NoDataExtension gameStatus={gameStatus} />;
return gameStatus === 'post'
? <PostGameExtension {...postGameProps} {...rest} />;
: <PreGameExtension {...preGameProps} {...rest} />;
}
}
view raw match-extension.js hosted with ❀ by GitHub

コンテナに耇数の状態が含たれる堎合、 composeうたく機胜したす。 しかし、状態に応じおのみ別のリク゚ストを実行する必芁がある堎合はどうでしょうか ここでskipは、䞊蚘の構成オブゞェクトで確認できるように圹立ちたす。 skipプロパティは、propsを受け取る関数を受け入れ、必芁な基準を満たさない堎合はク゚リの実行をスキップできたす。


これらすべおの䟋は、Reduxを知っおいれば、すぐにApolloの開発に参加できるこずを瀺しおいたす そのAPIにはReduxの抂念の倚くが組み蟌たれおいたすが、同じ結果を達成するために蚘述する必芁があるコヌドの量は削枛されおいたす。




メゞャヌリヌグサッカヌをアポロに移した経隓があなたのお圹に立おば幞いです さたざたなラむブラリに関する決定ず同様に、アプリケヌションでのデヌタの受信を制埡する最適な決定は、プロゞェクトの特定の芁件によっお異なりたす。 私たちの経隓に関しお質問がある堎合は、 ここにコメントを残すか、 Twitterでフォロヌしおください


読んでくれおありがずう



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


All Articles