文書化されていないReact.jsの世界。 コンテキスト

Habrahabrの読者に記事「文書化されていないreact.jsの地:コンテキスト」の翻訳を提供します。



Reactコンポーネントを見ると、いくつかのプロパティを見ることができます。



都道府県



はい、すべてのReactコンポーネントには状態があります。 これはコンポーネント内にあるものです。 コンポーネント自体のみが独自の状態で読み書きできます。名前が示すように、状態はコンポーネントの状態(Hello、Cap)を格納するために使用されます。 面白くない、続けましょう。



小道具



または、たとえば、プロパティ。 小道具は、コンポーネントの表示と動作に影響するデータです。 小道具はオプションまたは必須のいずれかで、親コンポーネントを介して提供されます。 理想的には、同じPropsをコンポーネントに渡すと、同じものがレンダリングされます。 面白くないので、話を進めましょう。



コンテキスト



コンテキスト、この投稿を書いた理由をご覧ください。 コンテキストは文書化されていないReact機能であり、小道具に似ていますが、違いは小道具が親コンポーネントから子コンポーネントに排他的に渡され、階層を伝播しないのに対し、コンテキストは子要素で単純に要求できることです。



しかし、どのように?



良い質問、描きましょう!







子Aおよび子Bコンポーネントをレンダリングする親Aコンポーネントをレンダリングする祖父母コンポーネントがあります。子Aおよび子Bが知りたいことを祖父母コンポーネントに知らせますが、親Aはそれを必要としません。 このデータをXdataと呼びましょう。 祖父母はどのようにしてXdataを子Aと子Bに渡しますか?



Fluxアーキテクチャを使用すると、Xdataをストア内に格納し、祖父母、子A、および子Bにこのストアにサインアップさせることができます。 しかし、子Aと子Bをマークアップをレンダリングするだけの愚かなコンポーネントにしたい場合はどうでしょうか?



さて、Xdataを小道具としてChild AとChild Bに渡すことができます。しかし、祖父母は、親Aに渡すことなく、Child AとChild Bに小道具をプッシュできません。3レベルのネストがある場合、これはそれほど大きな問題ではありません。しかし、実際のアプリケーションでは、上位コンポーネントがコンテナのように動作し、下位コンポーネントが通常のマークアップのように動作する、ネストのレベルがはるかに多くあります。 さて、ミックスインを使用して小道具が自動的に階層を下に移動するようにできますが、これはエレガントなソリューションではありません。



または、コンテキストを使用できます。 前に言ったように、コンテキストは子コンポーネントがいくつかのデータを要求できるようにして、データが階層の上位にあるコンポーネントから来るようにします。



それはどのように見えますか:



var Grandparent = React.createClass({ childContextTypes: { name: React.PropTypes.string.isRequired }, getChildContext: function() { return {name: 'Jim'}; }, render: function() { return <Parent/>; } }); var Parent = React.createClass({ render: function() { return <Child/>; } }); var Child = React.createClass({ contextTypes: { name: React.PropTypes.string.isRequired }, render: function() { return <div>My name is {this.context.name}</div>; } }); React.render(<Grandparent/>, document.body);
      
      





そして、ここにコード付きのJSBinがあります。 JimをJackに変更すると、コンポーネントがどのようにレンダリングされるかがわかります。



どうした



私たちの祖父母コンポーネントは2つのことを言っています:



1.文字列プロパティ(コンテキストタイプ)名を子孫に提供します。 これは、childContextTypesの宣言で行われます。

2.(コンテキストタイプ)nameプロパティの値はJimです。 これがgetChildContextメソッドで発生することです。



そして、私たちの子コンポーネントは「ちょっと、コンテキスト型の名前を期待しています!」と言うだけです。 私が理解している限り(私はReact.jsの内部の専門家からはほど遠い)、reactが子コンポーネントをレンダリングするとき、どのコンポーネントがコンテキストを必要とし、それを必要とするコンポーネントをチェックします-親コンポーネントが許可する場合に取得します(コンテキストを提供します)。



かっこいい!



はい、次のエラーが発生したらお待ちください。



 Warning: Failed Context Types: Required context `name` was not specified in `Child`. Check the render method of `Parent`. runner-3.34.3.min.js:1 Warning: owner-based and parent-based contexts differ (values: `undefined` vs `Jim`) for key (name) while mounting Child (see: http://fb.me/react-context-by-parent)
      
      





はい、もちろん、リンクを確認しましたが、あまり役に立ちません。



このコードがこのJSBinの理由です。



 var App = React.createClass({ render: function() { return ( <Grandparent> <Parent> <Child/> </Parent> </Grandparent> ); } }); var Grandparent = React.createClass({ childContextTypes: { name: React.PropTypes.string.isRequired }, getChildContext: function() { return {name: 'Jim'}; }, render: function() { return this.props.children; } }); var Parent = React.createClass({ render: function() { return this.props.children; } }); var Child = React.createClass({ contextTypes: { name: React.PropTypes.string.isRequired }, render: function() { return <div>My name is {this.context.name}</div>; } }); React.render(<App/>, document.body);
      
      





これは今では意味がありません。 投稿の最後に、実行可能な階層を設定する方法を説明します。



何が起こっているのかを理解するのに長い時間がかかりました。 問題をグーグルしようとする試みは、この問題に直面した人々の議論によってのみ与えられました。 エラーが何であるかをようやく理解したときに、コンテキストを使用してデータをコンポーネントツリーにプッシュするreact-routerやreact-reduxのような他のプロジェクトを見ました。



各コンポーネントには状態、小道具、コンテキストがあると言ったことを覚えていますか? 各コンポーネントには、いわゆる親と所有者もいます。 そして、警告からリンクをたどる(そう、はい、それは役に立つ、嘘をついた)ことを理解できます:



つまり、所有者は、親がDOMツリーの上位にあるコンポーネントである場合、コンポーネントを作成した所有者です。



この声明を理解するのに時間がかかりました。



したがって、最初の例では、Childコンポーネントの所有者はParentであり、Childコンポーネントの親もParentです。 2番目の例では、親が親である場合、子コンポーネントの所有者はAppです。



コンテキストは、奇妙な方法ですべての子孫に拡張されますが、明示的にそれを要求したコンポーネントからのみ利用可能です。 ただし、コンテキストは親から配布されるのではなく、所有者から配布されます。 また、子コンポーネントの所有者はアプリであり、Reactは親または祖父母ではなくアプリコンテキストで名前プロパティを見つけようとしています。



Reactの対応するバグレポートを次に示します。 そして、React 0.14の親に基づいてコンテキストを修正するプルリクエスト



ただし、React 0.14はまだありません。 修正(JSBin)



 var App = React.createClass({ render: function() { return ( <Grandparent> { function() { return (<Parent> <Child/> </Parent>) }} </Grandparent> ); } }); var Grandparent = React.createClass({ childContextTypes: { name: React.PropTypes.string.isRequired }, getChildContext: function() { return {name: 'Jack'}; }, render: function() { var children = this.props.children; children = children(); return children; } }); var Parent = React.createClass({ render: function() { return this.props.children; } }); var Child = React.createClass({ contextTypes: { name: React.PropTypes.string.isRequired }, render: function() { return <div>My name is {this.context.name}</div>; } }); React.render(<App/>, document.body);
      
      





アプリ内の親および子コンポーネントのインスタンスの代わりに、関数を返します。 次に、Grandparent内でこの関数を呼び出します。したがって、GrandparentをParentおよびChildコンポーネントの所有者にします。 コンテキストは必要に応じて広がります。



わかりましたが、なぜですか?



反応のローカライズに関する以前の記事を覚えていますか? 次の階層が考慮されました。



 <Application locale="en"> <Dashboard> <SalesWidget> <LocalizedMoney currency="USD">3133.7</LocalizedMoney> </SalesWidget> </Dashboard> </Application>
      
      





これは静的な階層ですが、通常はルーティングがあり、最終的には下位コンポーネントの所有者と親が異なる状況を作成します。



アプリケーションは、ロケールの読み込みとjquery / globalizeインスタンスの初期化を担当しますが、それらを使用しません。 トップレベルのコンポーネントをローカライズしません。 通常、ローカライズは、テキストノード、数字、金額、時間などの最下位のコンポーネントに影響します。 そして、グローバル化のインスタンスをコンポーネントツリーにドラッグする3つの可能な方法について以前に話しました。



グローバル化をストアに保存し、最下位のコンポーネントがこのストアをサブスクライブできるようにしますが、これは間違っていると思います。 下部のコンポーネントは、清潔で物のないものでなければなりません。



グローバル化インスタンスを小道具としてドラッグするのは面倒です。 すべてのコンポーネントがグローバル化を必要とすることを想像してください。 これは、グローバル変数globalizeを作成し、誰がそれを必要とし、それを使用できるようにすることに似ています。



しかし、最もエレガントな方法はコンテキストを使用することです。 アプリケーションコンポーネントには「ねえ、グローバル化インスタンスがあります。必要に応じて、私に知らせてください」と下位コンポーネントが「私に! 彼が必要だ!」 これはエレガントなソリューションです。 下位のコンポーネントはクリーンなままで、ストアに依存しません(はい、コンテキストに依存しますが、正しくレンダリングする必要があるため、そうする必要があります)。 グローバル化インスタンスは、階層全体の小道具を通過しません。 誰もが幸せです。



All Articles