編集:ES6の更新された例の最後の例を参照してください。
この回答は、直接の親子関係のケースを処理するだけです。親と子が仲介者を潜在的に多く持っている場合は、この回答を確認してください。
他の解決策は要点を逃している
それでも問題なく機能しますが、他の回答には非常に重要なものが欠けています。
React.jsで、イベントを使用して子の小道具を親に渡す簡単な方法はありませんか?
親はすでにその子支柱を持っています!:子供が小道具を持っている場合、それはその親がその小道具を子供に提供したためです!なぜ親が小道具を親に戻すのに、親がその小道具をすでに持っているのですか?
より良い実装
子供:それは本当にそれより複雑である必要はありません。
var Child = React.createClass({
render: function () {
return <button onClick={this.props.onClick}>{this.props.text}</button>;
},
});
単一の子を持つ親:子に渡す値を使用
var Parent = React.createClass({
getInitialState: function() {
return {childText: "Click me! (parent prop)"};
},
render: function () {
return (
<Child onClick={this.handleChildClick} text={this.state.childText}/>
);
},
handleChildClick: function(event) {
// You can access the prop you pass to the children
// because you already have it!
// Here you have it in state but it could also be
// in props, coming from another parent.
alert("The Child button text is: " + this.state.childText);
// You can also access the target of the click here
// if you want to do some magic stuff
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
子のリストを持つ親:親に必要なすべてのものがまだあり、子をより複雑にする必要はありません。
var Parent = React.createClass({
getInitialState: function() {
return {childrenData: [
{childText: "Click me 1!", childNumber: 1},
{childText: "Click me 2!", childNumber: 2}
]};
},
render: function () {
var children = this.state.childrenData.map(function(childData,childIndex) {
return <Child onClick={this.handleChildClick.bind(null,childData)} text={childData.childText}/>;
}.bind(this));
return <div>{children}</div>;
},
handleChildClick: function(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
});
JsFiddle
使用this.handleChildClick.bind(null,childIndex)
してから使用することも可能ですthis.state.childrenData[childIndex]
null
コンテキストにバインドしていることに注意してください。そうでない場合、Reactは自動バインドシステムに関連する警告を発行します。nullを使用すると、関数のコンテキストを変更したくないことになります。もご覧ください。
他の回答でのカプセル化と結合について
これは私にとってカップリングとカプセル化に関して悪い考えです:
var Parent = React.createClass({
handleClick: function(childComponent) {
// using childComponent.props
// using childComponent.refs.button
// or anything else using childComponent
},
render: function() {
<Child onClick={this.handleClick} />
}
});
小道具の使用:上記で説明したように、親には小道具がすでにあるため、子コンポーネント全体を渡して小道具にアクセスしても意味がありません。
参照の使用:イベントにはすでにクリックターゲットがあり、ほとんどの場合これで十分です。さらに、子に対して直接refを使用することもできます。
<Child ref="theChild" .../>
そして、親のDOMノードに
React.findDOMNode(this.refs.theChild)
親の子の複数の参照にアクセスするより高度なケースでは、子はすべてのdomノードをコールバックで直接渡すことができます。
コンポーネントにはインターフェース(プロパティ)があり、親は子の内部DOM構造や、それが参照を宣言するDOMノードを含めて、子の内部動作について何も想定しないでください。親が子の参照を使用しているということは、2つのコンポーネントを密結合しているということです。
問題を説明するために、ブラウザー内でスライダー、スクロールバー、ビデオプレーヤーなどをレンダリングするために使用されるShadow DOMについて、この引用を取り上げます。
彼らは、あなた、つまりWeb開発者が到達できるものと、実装の詳細と見なされるものとの間に境界を作り、したがって、あなたはアクセスできません。ただし、ブラウザはこの境界を自由にたどることができます。この境界を設けることで、彼らは、あなたがそうするように、divとspanから、同じ古き良きWebテクノロジーを使用してすべてのHTML要素を構築することができました。
問題は、子の実装の詳細を親にリークさせると、親に影響を与えずに子をリファクタリングすることが非常に困難になることです。これは、ライブラリの作成者(またはShadow DOMを使用するブラウザーエディター)として、クライアントにアクセスを許可しすぎて、互換性を損なうことなくコードをアップグレードすることが非常に困難になるため、非常に危険であることを意味します。
Chromeがクライアントにそのスクロールバーの内部domノードにアクセスできるようにするスクロールバーを実装した場合、これは、クライアントがそのスクロールバーを単に壊す可能性があることを意味し、Chromeがリファクタリング後に自動更新を実行すると、アプリがより簡単に壊れることになりますスクロールバー...代わりに、CSSを使用してスクロールバーの一部をカスタマイズするなど、安全なものへのアクセスのみを提供します。
その他の使用について
コールバックでコンポーネント全体を渡すのは危険であり、初心者の開発者がchildComponent.setState(...)
やなどの非常に奇妙なことをしたりchildComponent.forceUpdate()
、親の内部に新しい変数を割り当てたりして、アプリ全体を推論するのをはるかに難しくします。
編集:ES6の例
現在多くの人がES6を使用しているため、ES6構文の同じ例を以下に示します
子供は非常に簡単なことができます:
const Child = ({
onClick,
text
}) => (
<button onClick={onClick}>
{text}
</button>
)
親はクラスにすることができます(最終的には状態自体を管理できますが、ここでは小道具として渡します:
class Parent1 extends React.Component {
handleChildClick(childData,event) {
alert("The Child button data is: " + childData.childText + " - " + childData.childNumber);
alert("The Child HTML is: " + event.target.outerHTML);
}
render() {
return (
<div>
{this.props.childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => this.handleChildClick(child,e)}
/>
))}
</div>
);
}
}
ただし、状態を管理する必要がない場合は、簡略化することもできます。
const Parent2 = ({childrenData}) => (
<div>
{childrenData.map(child => (
<Child
key={child.childNumber}
text={child.childText}
onClick={e => {
alert("The Child button data is: " + child.childText + " - " + child.childNumber);
alert("The Child HTML is: " + e.target.outerHTML);
}}
/>
))}
</div>
)
JsFiddle
PERF WARNING(ES5 / ES6に適用):PureComponent
またはを使用している場合shouldComponentUpdate
、上記の実装はデフォルトで最適化されませんonClick={e => doSomething()}
。これは、親をレンダリングするたびに新しい関数が作成されるため、レンダーフェーズで直接使用またはバインドするためです。これがアプリのパフォーマンスのボトルネックである場合は、データを子に渡し、「安定した」コールバック(親クラスで設定し、this
クラスコンストラクターでバインド)内に再注入して、PureComponent
最適化を開始できるようにするか、または独自に実装shouldComponentUpdate
し、小道具の比較チェックでコールバックを無視できます。
使用することもでき構図の微調整最適化を実現する高次成分を提供ライブラリを、:
// A component that is expensive to render
const ExpensiveComponent = ({ propA, propB }) => {...}
// Optimized version of same component, using shallow comparison of props
// Same effect as React's PureRenderMixin
const OptimizedComponent = pure(ExpensiveComponent)
// Even more optimized: only updates if specific prop keys have changed
const HyperOptimizedComponent = onlyUpdateForKeys(['propA', 'propB'])(ExpensiveComponent)
この場合、以下を使用して子コンポーネントを最適化できます。
const OptimizedChild = onlyUpdateForKeys(['text'])(Child)