React.jsの親コンポーネントに小道具を渡す


295

propsReact.jsで、イベントを使用して子を親に渡す簡単な方法はありませんか?

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(event) {
    // event.component.props ?why is this not available?
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

制御されたコンポーネントを使用して入力の値を渡すことができることは知っていますが、キット全体のkaboodleを渡すとよいでしょう。子コンポーネントには、検索する必要がない一連の情報が含まれている場合があります。

おそらく、コンポーネントをイベントにバインドする方法はありますか?

更新– 2015年9月1日

使用後一年以上にわたって反応し、セバスチャン・ローバーの答えでオンに拍車をかけ、私は両親に関数に引数として子コンポーネントを渡すことであると結論付けてきたではないように反応することで、これまでにも良いアイデアそれでした。答えを切り替えました。


同様の問題(ここここ)にはさまざまな答えがありますが、特にエレガントなものはありません
Bojangles

同意します。イベントをチェーンに渡すのは素晴らしいことですが、イベントを発生させたコンポーネントを確認するのは素晴らしいことです。
KendallB 2014年

受け入れられた答えでは不十分だと思うので、私の答えを見てください。stackoverflow.com/a/31756470/82609
Sebastien Lorber、2015

1
kit n 'kaboodle-すべてを含めるため。すべてのベルとホイッスルを取得します。コンボネーション。urbandictionary.com/define.php?term=kit%20and%20caboodle
Filip Bartuzi

回答:


268

編集: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)

1
ええ、昨年、Reactを使用して過ごしたので、私は同意します。インスタンス化されたコンポーネントを関数経由で渡す理由はありません。私は自分の答えを廃止するつもりです。私はまた、おそらくいくつかの変更を加えて、回答を切り替えてもかまいません。「より良い実装」は、この質問が生じた実際の理由、つまり「どの子供がグループから選ばれたかを知るにはどうすればよいですか」に対処していません。Fluxは適切なサイズのアプリの答えですが、小さなアプリでは、上の関数を介してコールチェーンをバックアップするために(コンポーネントではなく)値を渡すと便利ですprops。同意する?
KendallB 2015

@KendallBは私の回答に同意してくれてうれしいです:) How do I know which of my children was chosen among a group?これは実際には元の質問の一部ではありませんが、私の回答に解決策を含めました。編集とは異なりbind、親で直接使用できるため、リストを処理する場合でも、子を変更する必要はまったくないと思います。
Sebastien Lorber

私はあなたの答えを編集しました、参照に関する部分。omerwazir.com/posts/react-getdomnode-replaced-with-findDOMNode
ffxsam

こんにちは@SebastienLorber私は未来から来ています。このラインについてonClick={this.handleChildClick.bind(null,childData)}について、あなたは言及自動バインディングすることに含まれていない反応することが今、このまだ適用されるクラスモデル。私はたくさんのことを読みましたが、この部分はまだ私を混乱させます。class modelちなみに私はES6を使用しているので、に変更nullthisてもコードはまだ機能しているようです。コードのその部分をどのようにES6スタイルに変換しますか?
JohnnyQ 2016年

また、これを最新のReactで機能させるのに苦労しています。何を変更する必要がありますか?
フセインドゥヴィニョウ2017年

143

更新(15/9/15):OPはこの質問を少しターゲットを移動させました。再び更新されました。だから、私は私の返信を更新する責任があると感じています。

まず、提供された例への回答:

はい、可能です。

Childを次のonClickように更新することでこれを解決できますthis.props.onClick.bind(null, this)

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick.bind(null, this)}>Click me</a>;
  }
});

親のイベントハンドラーは、コンポーネントとイベントに次のようにアクセスできます。

  onClick: function (component, event) {
    // console.log(component, event);
  },

JSBinスナップショット


しかし、質問自体は誤解を招く

親は子供のことを知っていpropsます。

実際には小道具が提供されていないため、提供された例ではこれは明確ではありません。このサンプルコードは、尋ねられる質問をより適切にサポートする可能性があります。

var Child = React.createClass({
  render: function () {
    return <a onClick={this.props.onClick}> {this.props.text} </a>;
  }
});

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (event) {
    // event.component.props ?why is this not available? 
  },
  render: function() {
    return <Child onClick={this.onClick} text={this.state.text} />;
  }
});

この例では、チャイルドの小道具が何であるかをすでに知っていることがより明確になります。

JSBinスナップショット


それが本当に子供の小道具を使用することであるなら…

それが本当に子供の小道具を使用することについてであるならば、あなたは完全に子供との接続を避けることができます。

JSXには、Childなどのコンポーネントでよく使用するスプレッド属性 API があります。すべてを受け取りprops、コンポーネントに適用します。子供は次のようになります:

var Child = React.createClass({
  render: function () {
    return <a {...this.props}> {this.props.text} </a>;
  }
});

親で直接値を使用できるようにする:

var Parent = React.createClass({
  getInitialState: function () {
    return { text: "Click here" };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />;
  }
});

JSBinスナップショット


追加の子コンポーネントを接続するため、追加の構成は必要ありません。

var Parent = React.createClass({
  getInitialState: function () {
    return {
      text: "Click here",
      text2: "No, Click here",
    };
  },
  onClick: function (text) {
    alert(text);
  },
  render: function() {
    return <div>
      <Child onClick={this.onClick.bind(null, this.state.text)} text={this.state.text} />
      <Child onClick={this.onClick.bind(null, this.state.text2)} text={this.state.text2} />
    </div>;
  }
});

JSBinスナップショット

しかし、それはあなたの実際のユースケースではないと思います。さらに掘り下げましょう…


堅牢で実用的な例

提供されている例の一般的な性質について話すのは難しいです。上記の質問の実際的な使用法を示すコンポーネントを作成し、非常にReactyの方法で実装しました。

DTServiceCalculatorの動作例
DTServiceCalculator repo

このコンポーネントは、単純なサービス計算機です。サービスのリスト(名前と価格)を提供すると、選択した価格の合計が計算されます。

子供たちは至福を知らない

ServiceItemこの例の子コンポーネントです。外の世界についてはあまり意見がありません。これは、いくつかの小道具が必要です、クリックされたときに呼び出される関数であるのいずれかを。

<div onClick={this.props.handleClick.bind(this.props.index)} />

handleClick提供されたindex[ source ]を使用して、提供されたコールバックを呼び出すだけです。

親は子供です

DTServicesCalculator親コンポーネントはこの例です。それも子供です。見てみよう。

DTServiceCalculator子コンポーネントのリストを作成ServiceItemし、props [ source ] を提供します。これは親コンポーネントですServiceItemが、リストを渡すコンポーネントの子コンポーネントです。データを所有していません。そのため、コンポーネントの処理を親コンポーネントのソースに委譲します

<ServiceItem chosen={chosen} index={i} key={id} price={price} name={name} onSelect={this.props.handleServiceItem} />

handleServiceItem子から渡されたインデックスを取得し、その親に提供します[ ソース ]

handleServiceClick (index) {
  this.props.onSelect(index);
}

所有者はすべてを知っています

「所有権」の概念は、Reactの重要な概念です。詳しくはこちらをご覧ください

私が示した例では、状態を所有するコンポーネントに到達するまで、コンポーネントツリーの上方でイベントの処理を委任し続けます。

最終的にそこに到達すると、状態の選択/選択解除を次のように処理します[ ソース ]:

handleSelect (index) {
  let services = […this.state.services];
  services[index].chosen = (services[index].chosen) ? false : true;
  this.setState({ services: services });
}


結論

最も外側のコンポーネントをできるだけ不透明にしてください。親コンポーネントがそれらを実装するためにどのように選択するかについて、設定がほとんどないことを確認するように努めてください。

操作しているデータを誰が所有しているかに注意してください。ほとんどの場合、イベント処理をツリーの上位に、その状態を所有するコンポーネントに委任する必要があります。

余談:Fluxパターンは、アプリでこの種の必要な接続を減らすための良い方法です。


簡潔な答えをありがとう、それは本当に私がReactを少しよく理解するのに本当に役立ちました!同じように質問があります。私はパブ/サブのためにフラックスを使用しています。例のようにChildイベントハンドラーをParentに渡す代わりに、これを「アクション」として実装してリッスンすることができます。これはFluxの良い代替手段であり使用方法だと思いますか?
Pathsofdesign 2014

1
@Pathsofdesignに感謝!それは依存するでしょう。Fluxには、コントローラービューという概念があります。この例でParentは、そのようなController-Viewである可能性がありますが、Child単なるdumb-View(コンポーネント)です。Controller-Viewsのみがアプリケーションの知識を持つ必要があります。この場合でも、アクションをからParentChildプロップとして渡します。アクション自体が関係しているため、Fluxには、アクションを操作してビューを更新するための強く規定されたパターンがあります。facebook.github.io/flux/docs/...
chantastic

1
onClick={this.onClick.bind(null, this.state.text)}親が持っているので、状態をバインドする必要はありません。だからonClick={this.onClick}うまくいきます。onClick = ()=>{const text = this.state.text;..}親のどこに
Shishir Arora

25

簡単な答えがあるようです。このことを考慮:

var Child = React.createClass({
  render: function() {
    <a onClick={this.props.onClick.bind(null, this)}>Click me</a>
  }
});

var Parent = React.createClass({
  onClick: function(component, event) {
    component.props // #=> {Object...}
  },
  render: function() {
    <Child onClick={this.onClick} />
  }
});

キーは、親から渡さbind(null, this)れたthis.props.onClickイベントを呼び出しています。これで、onClick関数は引数componentANDを受け入れますevent。それはすべての世界で最高だと思います。

更新:2015年9月1日

これは悪い考えでした。子の実装の詳細を親に漏らさせることは決して良い方法ではありませんでした。セバスチャン・ローバーの答えを見てください。


引数を間違った方法で取り替えましたか(入れ替えた場所)?
2015

1
@TheSurricanいいえ、まったく問題ありません。最初の引数はthis関数内にバインドされ(いずれにしても必要ありません)、onClickが呼び出されたときに2番目の引数が最初の引数として追加されます。
manmal 2015

13

問題は、子コンポーネントから親コンポーネントに引数を渡す方法です。この例は使いやすく、テストも簡単です。

//Child component
class Child extends React.Component {
    render() {
        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>Push me</button></div>
        )
    }
}

//Parent component
class Parent extends React.Component {
    constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
    }

    handleToUpdate(someArg){
        alert('We pass argument from Child to Parent: \n' + someArg);
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;
        return (<div>
          <Child handleToUpdate = {handleToUpdate.bind(this)} />
        </div>)
    }
}

if(document.querySelector("#demo")){
    ReactDOM.render(
        <Parent />,
        document.querySelector("#demo")
    );
}

JSFIDDLEを見てください


私の場合、コールバック関数をclick()に渡すのを忘れています
Long Nguyen

6

基本的に、小道具を使用して子供と親との間で情報を送信します。

すべての素晴らしい答えに加えて、Reactで子コンポーネントから親コンポーネントに値を渡すことを説明する簡単な例を挙げましょう

App.js

class App extends React.Component {
      constructor(){
            super();
            this.handleFilterUpdate = this.handleFilterUpdate.bind(this);
            this.state={name:'igi'}
      }
      handleFilterUpdate(filterValue) {
            this.setState({
                  name: filterValue
            });
      }
   render() {
      return (
        <div>
            <Header change={this.handleFilterUpdate} name={this.state.name} />
            <p>{this.state.name}</p>
        </div>
      );
   }
}

Header.js

class Header extends React.Component {
      constructor(){
            super();
            this.state={
                  names: 'jessy'
            }
      }
      Change(event) {

      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

   render() {
      return (
       <button onClick={this.Change.bind(this)}>click</button>

      );
   }
}

Main.js

import React from 'react';
import ReactDOM from 'react-dom';

import App from './App.jsx';

ReactDOM.render(<App />, document.getElementById('app'));

これで、クライアントからサーバーに値を渡すことができます。

Header.jsのChange関数を見てください。

Change(event) {
      // this.props.change(this.state.names);
      this.props.change('jessy');
  }

これは、クライアントからサーバーに小道具に値をプッシュする方法です。


これがクライアントからサーバーに小道具に値をプッシュする方法です。このステートメントの意味は何ですか
G1P

2

これは、親コンストラクターで関数バインディングを使用する簡単な3ステップES6実装です。これは、公式の反応チュートリアルが推奨する最初の方法です(ここではカバーされていないパブリッククラスフィールドの構文もあります)。この情報はすべてhttps://reactjs.org/docs/handling-events.htmlにあります。

子供がそれらを呼び出すことができるように親関数をバインドする(そして親にデータを渡す!:D)

  1. 親コンストラクターで、親で作成した関数をバインドしていることを確認してください
  2. バインドされた関数を子としてプロップとして渡します(関数への参照を渡すため、ラムダはありません)。
  3. バインドされた関数を子イベントから呼び出します(Lambda!イベントが発生したときに関数を呼び出しています。これを行わない場合、関数はロード時に自動的に実行され、イベントでトリガーされません)。

親機能

handleFilterApply(filterVals){} 

親コンストラクタ

this.handleFilterApply = this.handleFilterApply.bind(this);

子供に渡された小道具

onApplyClick = {this.handleFilterApply}

子供イベント呼び出し

onClick = {() => {props.onApplyClick(filterVals)}

0

これは、onClickイベントを使用しない例です。小道具でコールバック関数を子に渡すだけです。そのコールバックでは、子呼び出しもデータを送り返します。私はドキュメントの例に触発されました。

小さな例(これはtsxファイルにあるため、propsとstateは完全に宣言する必要があります。コンポーネントからいくつかのロジックを削除したため、コードが少なくなっています)。

*更新:重要なのは、これをコールバックにバインドすることです。それ以外の場合、コールバックには、親ではなく子のスコープがあります。唯一の問題:「古い」親です...

SymptomChoserが親です。

interface SymptomChooserState {
  // true when a symptom was pressed can now add more detail
  isInDetailMode: boolean
  // since when user has this symptoms
  sinceDate: Date,
}

class SymptomChooser extends Component<{}, SymptomChooserState> {

  state = {
    isInDetailMode: false,
    sinceDate: new Date()
  }

  helloParent(symptom: Symptom) {
    console.log("This is parent of: ", symptom.props.name);
    // TODO enable detail mode
  }

  render() {
    return (
      <View>
        <Symptom name='Fieber' callback={this.helloParent.bind(this)} />
      </View>
    );
  }
}

症状は子です(コールバック関数を宣言した子の小道具で、関数selectedSymptomでコールバックが呼び出されます):

interface SymptomProps {
  // name of the symptom
  name: string,
  // callback to notify SymptomChooser about selected Symptom.
  callback: (symptom: Symptom) => void
}

class Symptom extends Component<SymptomProps, SymptomState>{

  state = {
    isSelected: false,
    severity: 0
  }

  selectedSymptom() {
    this.setState({ isSelected: true });
    this.props.callback(this);
  }

  render() {
    return (
      // symptom is not selected
      <Button
        style={[AppStyle.button]}
        onPress={this.selectedSymptom.bind(this)}>
        <Text style={[AppStyle.textButton]}>{this.props.name}</Text>
      </Button>
    );
  }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.