多くのテンプレート言語は "スロット"または "イールド"ステートメントを持っています。それはあるテンプレートを別のテンプレートの中に包むためにある種の制御の逆転をすることを可能にします。
Angularは "transclude"オプション を持っています。
Railsは yieldステートメント を持っています。 React.jsにyieldステートメントがあれば、これは次のようになります。
var Wrapper = React.createClass({
render: function() {
return (
<div className="wrapper">
before
<yield/>
after
</div>
);
}
});
var Main = React.createClass({
render: function() {
return (
<Wrapper><h1>content</h1></Wrapper>
);
}
});
望ましい出力:
<div class="wrapper">
before
<h1>content</h1>
after
</div>
残念ながら、React.jsに<yield/>
はありません。同じ出力を得るためにWrapperコンポーネントをどのように定義すればよいですか?
試してください:
var Wrapper = React.createClass({
render: function() {
return (
<div className="wrapper">
before
{this.props.children}
after
</div>
);
}
});
詳しくは、ドキュメント内の Multiple Components:Children および Type of the Childrenの小道具 を参照してください。
children
を使うconst Wrapper = ({children}) => (
<div>
<div>header</div>
<div>{children}</div>
<div>footer</div>
</div>
);
const App = ({name}) => <div>Hello {name}</div>;
const WrappedApp = ({name}) => (
<Wrapper>
<App name={name}/>
</Wrapper>
);
render(<WrappedApp name="toto"/>,node);
これはAngularではtransclusion
としても知られています。
children
はReactの特別な小道具で、あなたのコンポーネントのタグの中にあるものを含みます(ここで<App name={name}/>
はWrapper
の中にあるので、それはchildren
です)
必ずしもコンポーネント固有のchildren
を使用する必要はありません。必要に応じて通常のプロップを使用することも、プロップと子を混在させることもできます。
const AppLayout = ({header,footer,children}) => (
<div className="app">
<div className="header">{header}</div>
<div className="body">{children}</div>
<div className="footer">{footer}</div>
</div>
);
const appElement = (
<AppLayout
header={<div>header</div>}
footer={<div>footer</div>}
>
<div>body</div>
</AppLayout>
);
render(appElement,node);
これは多くのユースケースにとってシンプルで素晴らしいものであり、私はこれをほとんどの消費者向けアプリケーションにお勧めします。
レンダリング関数をコンポーネントに渡すことは可能です。このパターンは一般に render prop
と呼ばれ、children
プロップはそのコールバックを提供するためによく使用されます。
このパターンは実際にはレイアウト用ではありません。ラッパーコンポーネントは通常、何らかの状態を保持および管理し、それをレンダリング機能に挿入するために使用されます。
反例:
const Counter = () => (
<State initial={0}>
{(val, set) => (
<div onClick={() => set(val + 1)}>
clicked {val} times
</div>
)}
</State>
);
あなたはさらに派手になることができてもオブジェクトを提供することができます
<Promise promise={somePromise}>
{{
loading: () => <div>...</div>,
success: (data) => <div>{data.something}</div>,
error: (e) => <div>{e.message}</div>,
}}
</Promise>
必ずしもchildren
を使用する必要はないことに注意してください。これは好み/ APIの問題です。
<Promise
promise={somePromise}
renderLoading={() => <div>...</div>}
renderSuccess={(data) => <div>{data.something}</div>}
renderError={(e) => <div>{e.message}</div>}
/>
今日の時点で、多くのライブラリはレンダープロップ(React context、React-motion、Apollo ...)を使用しています。なぜなら、人々はこのAPIをHOCよりも簡単に見つける傾向があるからです。 react-powerplug は単純なrender-propコンポーネントの集まりです。 反応 - 採用 あなたが作曲をするのを助けます。
const wrapHOC = (WrappedComponent) => {
class Wrapper extends React.PureComponent {
render() {
return (
<div>
<div>header</div>
<div><WrappedComponent {...this.props}/></div>
<div>footer</div>
</div>
);
}
}
return Wrapper;
}
const App = ({name}) => <div>Hello {name}</div>;
const WrappedApp = wrapHOC(App);
render(<WrappedApp name="toto"/>,node);
Higher-Order Component/HOC は一般に、コンポーネントを受け取り新しいコンポーネントを返す関数です。
高次コンポーネントを使用すると、children
またはrender props
を使用するよりもパフォーマンスが向上します。これは、ラッパーがshouldComponentUpdate
を使用して1段階先にレンダリングを短縮できるためです。
ここではPureComponent
を使用しています。アプリを再レンダリングするときに、WrappedApp
という名前の小道具が時間の経過とともに変化しない場合、ラッパーは「小道具(実際には、名前)が以前と同じなのでレンダリングする必要はありません」と言うことができます。上のchildren
ベースの解決法では、ラッパーがPureComponent
であっても、親要素がレンダリングされるたびに子要素が再作成されるため、そうではありません。つまり、ラップされたコンポーネントが純粋でもこれを軽減し、時間の経過とともに一定のchildren
要素を確保するのを助けることができる babel plugin があります。
高次コンポーネントを使用すると、パフォーマンスが向上します。それほど複雑ではありませんが、最初は確かに非友好的に見えます。
これを読んだ後は、コードベース全体をHOCに移行しないでください。アプリケーションのクリティカルパスでは、パフォーマンス上の理由からランタイムラッパーの代わりにHOCを使用することをお勧めします。特に、同じラッパーが頻繁に使用される場合は、HOCにすることを検討する価値があります。
Reduxは、最初はランタイムラッパー<Connect>
を使用し、後でパフォーマンス上の理由でHOC connect(options)(Comp)
に切り替えました(デフォルトでは、ラッパーは純粋でshouldComponentUpdate
を使用します)。これは私がこの答えで強調したかったことの完璧な実例です。
コンポーネントにrender-prop APIがある場合、その上にHOCを作成するのは一般的に簡単です。そのため、もしあなたがlibの作者なら、最初にrender prop APIを書いて、最終的にHOCバージョンを提供するべきです。これがApolloが<Query>
render-propコンポーネント、およびそれを使用するgraphql
HOCに対して行うことです。
個人的には両方を使用していますが、疑問がある場合はHOCを好みます。
compose(hoc1,hoc2)(Comp)
)私は私のお気に入りのツールのHOCバージョンを使用/作成することを躊躇しません。
Context.Consumer
compSubscribe
graphql
render propの代わりにQuery
をApolloのHOCを使用する私の意見では、時々レンダリングプロップはコードをより読みやすくし、時にはより少なくします...私は私が持っている制約に従って最も実用的な解決策を使おうとします。読みやすさがパフォーマンスよりも重要な場合もあれば、そうでない場合もあります。賢明に選択し、すべてをレンダープロップに変換する2018年のトレンドに密接に従ってはいけません。
Sophieの答えに加えて、私は次のようなことをして、子コンポーネントタイプを送信することにも用途を見出しました。
var ListView = React.createClass({
render: function() {
var items = this.props.data.map(function(item) {
return this.props.delegate({data:item});
}.bind(this));
return <ul>{items}</ul>;
}
});
var ItemDelegate = React.createClass({
render: function() {
return <li>{this.props.data}</li>
}
});
var Wrapper = React.createClass({
render: function() {
return <ListView delegate={ItemDelegate} data={someListOfData} />
}
});