Reactで親の状態を更新する方法は?


349

私の構造は次のようになります:

Component 1  

 - |- Component 2


 - - |- Component 4


 - - -  |- Component 5  

Component 3

コンポーネント3は、コンポーネント5の状態に応じていくつかのデータを表示する必要があります。プロップは不変なので、コンポーネント1にその状態を保存して転送することはできません。そして、はい、私はreduxについて読みましたが、それを使いたくありません。反応だけで解決できることを願っています。私が間違っている?


20
super-easy:プロパティを介してparent-setState-Functionを子コンポーネントに渡します:<MyChildComponent setState = {p => {this.setState(p)}} />子コンポーネントでthis.propsを介して呼び出します。 setState({myObj、...});
Marcel Ennix、2018年

@MarcelEnnix、あなたのコメントは私の時間を節約します。ありがとう。
Dinith Minura

<MyChildComponent setState={(s,c)=>{this.setState(s, c)}} />このハックを使用する場合は、コールバックをサポートしていることを確認してください。
Barkermn01 2019

4
親の状態を設定するためにコールバックを渡すことは、メンテナンスの問題につながる可能性がある本当に悪い習慣です。カプセル化が解除され、コンポーネント2 4および5が1に密結合されます。このパスをたどると、これらの子コンポーネントを他の場所で再利用できなくなります。特定の小道具を用意して、子コンポーネントが何かが発生したときにイベントをトリガーできるようにすると、親コンポーネントがそのイベントを適切に処理できるようになります。
Pato Loco、

@MarcelEnnix、なぜ中括弧が周りにあるのthis.setState(p)ですか?私はそれらなしで試しましたが、動作するようです(Reactは非常に新しいです)
Biganon

回答:


677

親子通信の場合、次のように、状態を設定する関数を親から子に渡す必要があります

class Parent extends React.Component {
  constructor(props) {
    super(props)

    this.handler = this.handler.bind(this)
  }

  handler() {
    this.setState({
      someVar: 'some value'
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {this.props.handler}/ >
  }
}

このように、子は、propsで渡された関数の呼び出しで親の状態を更新できます。

ただし、コンポーネント5と3は関係ないので、コンポーネントの構造を再考する必要があります。

1つの可能な解決策は、コンポーネント1と3の両方の状態を含む上位レベルのコンポーネントでそれらをラップすることです。このコンポーネントは、propsを介して下位レベルの状態を設定します。


6
なぜ、状態を設定するハンドラー関数だけでなく、this.handler = this.handler.bind(this)が必要なのですか?
chemook78 2017

34
ES6 Reactクラスのメソッドの@ chemook78は、クラスに自動バインドしません。したがってthis.handler = this.handler.bind(this)、コンストラクタを追加しない場合thishandler関数内ではクラスではなく関数クロージャが参照されます。すべての関数をコンストラクターにバインドしたくない場合は、矢印関数を使用してこれに対処する方法が2つあります。クリックハンドラーを次のように記述するonClick={()=> this.setState(...)}か、プロパティの初期化子と矢印関数を一緒に使用して、「矢印関数」のbabeljs.io/blog/2015/06/07/react-on-es6-plusで説明されているように
Ivan

1
ここではこの動作の例です:plnkr.co/edit/tGWecotmktae8zjS5yEr?p=previewは
TAMB

5
それはすべて理にかなっている、なぜe.preventDefaultなのか?そしてそれはjqueryを必要としますか?
Vincent Buscarello 2017

1
簡単な質問ですが、これは子供の内部の州の割り当てを禁止しますか?
ThisGuyCantEven

50

子から親コンポーネントにonClick関数の引数を渡す次の実用的な解決策を見つけました。

method()を渡すバージョン

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div><button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component {

    constructor(props) {
        super(props);
        var handleToUpdate  = this.handleToUpdate.bind(this);
        var arg1 = '';
    }

    handleToUpdate(someArg){
            alert('We pass argument from Child to Parent: ' + someArg);
            this.setState({arg1:someArg});
    }

    render() {
        var handleToUpdate  =   this.handleToUpdate;

        return (<div>
                    <ChildB handleToUpdate = {handleToUpdate.bind(this)} /></div>)
    }
}

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

JSFIDDLEを見てください

Arrow関数を渡すバージョン

//ChildB component
class ChildB extends React.Component {

    render() {

        var handleToUpdate  =   this.props.handleToUpdate;
        return (<div>
          <button onClick={() => handleToUpdate('someVar')}>
            Push me
          </button>
        </div>)
    }
}

//ParentA component
class ParentA extends React.Component { 
    constructor(props) {
        super(props);
    }

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

    render() {
        return (<div>
            <ChildB handleToUpdate = {this.handleToUpdate} /></div>)
    }
}

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

JSFIDDLEを見てください


これはいい!この作品について説明してください:<ChildB handleToUpdate = {handleToUpdate.bind(this)} />なぜ再度拘束しなければならなかったのですか?
デーン

@Dane-このコンテキストを親にバインドしてthis、子の中で呼び出されたときに、子thisの状態ではなく親の状態を参照するようにする必要があります。これは最高級の閉鎖です!
ケイシー

@Caseyですが、コンストラクタでそれを行っていませんか?そしてそれで十分ではありませんか?
デーン

あなたは完全に正しいです!私は逃しました。はい、コンストラクタで既に実行している場合は、それで問題ありません。
ケーシー

あなたは伝説の仲間です!これは、うまく自己状態の交換を処理するために、親コンポーネントを作成することを余儀なくされることなく含まれる成分おこう
adamj

13

私は自分の問題のアイデアを基本的に矢印関数で変更し、子コンポーネントからparamを渡すための最も支持された回答に感謝します:

 class Parent extends React.Component {
  constructor(props) {
    super(props)
    // without bind, replaced by arrow func below
  }

  handler = (val) => {
    this.setState({
      someVar: val
    })
  }

  render() {
    return <Child handler = {this.handler} />
  }
}

class Child extends React.Component {
  render() {
    return <Button onClick = {() => this.props.handler('the passing value')}/ >
  }
}

それが誰かを助けることを願っています。


直接呼び出しの矢印関数について特別なものは何ですか?
Ashish Kamble

@AshishKambleのインアローthis関数は、親のコンテキスト(Parentクラス)を参照します。
CPHPython

これは重複した答えです。受け入れられた回答にコメントを追加し、この実験的な機能について言及して、クラスで矢印関数を使用できます。
Arashsoft

10

関数の受け渡しに関する答えはとても便利です。これはとても便利なテクニックです。

裏側では、Fluxと同様に、pub / subを使用するか、ディスパッチャのバリアントを使用してこれを実現することもできます。理論は非常に単純で、コンポーネント5がコンポーネント3がリッスンしているメッセージをディスパッチします。次に、コンポーネント3はその状態を更新し、再レンダリングをトリガーします。これには、ステートフルなコンポーネントが必要です。これは、視点に応じて、アンチパターンである場合とそうでない場合があります。私は個人的に彼らに反対しており、他の何かがディスパッチをリッスンしていて、状態をトップダウンから変更することを望んでいます(Reduxはこれを行いますが、追加の用語を追加します)。

import { Dispatcher } from flux
import { Component } from React

const dispatcher = new Dispatcher()

// Component 3
// Some methods, such as constructor, omitted for brevity
class StatefulParent extends Component {
  state = {
    text: 'foo'
  } 

  componentDidMount() {
    dispatcher.register( dispatch => {
      if ( dispatch.type === 'change' ) {
        this.setState({ text: 'bar' })
      }
    }
  }

  render() {
    return <h1>{ this.state.text }</h1>
  }
}

// Click handler
const onClick = event => {
  dispatcher.dispatch({
    type: 'change'
  })
}

// Component 5 in your example
const StatelessChild = props => {
  return <button onClick={ onClick }>Click me</button> 
}

Fluxのディスパッチャバンドルは非常にシンプルです。ディスパッチャがコールバックを登録し、ディスパッチが発生したときにそれらを呼び出し、ディスパッチのコンテンツを渡します(上記の簡潔な例ではpayload、ディスパッチにはありません。単なるメッセージIDです)。これが従来のpub / subに(たとえば、イベントからのEventEmitterや他のバージョンを使用して)非常に簡単に適応できる場合は、これをより簡単に適用できます。


私のReactsコンポーネントは、公式チュートリアル(facebook.github.io/react/docs/tutorial.html)のようにブラウザーで "実行中"です。私はbrowserifyでFluxを含めようとしましたが、ブラウザーはDispatcherが見つからないと言っています:(
wklm

2
私が使用した構文は、トランスパイレーションを必要とするES2016モジュールの構文でした(私はBabelを使用していますが、他にもありますが、babelifyトランスフォームはbrowserifyで使用できます)。それは次のように変換されますvar Dispatcher = require( 'flux' ).Dispatcher
Matt Styles

8

子から親コンポーネントにparamでonClick関数引数を渡すための次の実用的な解決策を見つけました:

親クラス:

class Parent extends React.Component {
constructor(props) {
    super(props)

    // Bind the this context to the handler function
    this.handler = this.handler.bind(this);

    // Set some state
    this.state = {
        messageShown: false
    };
}

// This method will be sent to the child component
handler(param1) {
console.log(param1);
    this.setState({
        messageShown: true
    });
}

// Render the child component and set the action property with the handler as value
render() {
    return <Child action={this.handler} />
}}

子クラス:

class Child extends React.Component {
render() {
    return (
        <div>
            {/* The button will execute the handler function set by the parent component */}
            <Button onClick={this.props.action.bind(this,param1)} />
        </div>
    )
} }

2
それが許容できる解決策であるかどうかは誰にもわかりますか(特に、提案されているようにパラメータを渡すことに関心があります)。
ilans

param1コンソールに表示するだけではなく、常に割り当てられますtrue
Ashish Kamble

ソリューションの品質について話すことはできませんが、これはパラメーターを正常に渡します。
ジェームズ

6

子から親への任意のレベルでの通信が必要な場合は常に、コンテキストを利用することをお勧めし ます。親コンポーネントで、次のような子が呼び出すことができるコンテキストを定義します

親コンポーネントで、ケースコンポーネント3

static childContextTypes = {
        parentMethod: React.PropTypes.func.isRequired
      };

       getChildContext() {
        return {
          parentMethod: (parameter_from_child) => this.parentMethod(parameter_from_child)
        };
      }

parentMethod(parameter_from_child){
// update the state with parameter_from_child
}

子コンポーネント(ここではコンポーネント5)で、親のコンテキストを使用することをこのコンポーネントに伝えます。

 static contextTypes = {
       parentMethod: React.PropTypes.func.isRequired
     };
render(){
    return(
      <TouchableHighlight
        onPress={() =>this.context.parentMethod(new_state_value)}
         underlayColor='gray' >   

            <Text> update state in parent component </Text>              

      </TouchableHighlight>
)}

あなたはレポでデモプロジェクトを見つけることができます


私はこの答えを理解できません。それについてもっと説明してもらえますか
Ashish Kamble

5

親から子にデータを渡すことができるのは、reactが単方向データフローを促進するためですが、「子コンポーネント」で何かが発生したときに親にそれ自体を更新させるために、通常「コールバック関数」と呼ばれるものを使用します。

親で定義された関数を "props"として子に渡し、親コンポーネントでトリガーする子からその関数を呼び出します。


class Parent extends React.Component {
  handler = (Value_Passed_From_SubChild) => {
    console.log("Parent got triggered when a grandchild button was clicked");
    console.log("Parent->Child->SubChild");
    console.log(Value_Passed_From_SubChild);
  }
  render() {
    return <Child handler = {this.handler} />
  }
}
class Child extends React.Component {
  render() {
    return <SubChild handler = {this.props.handler}/ >
  }
}
class SubChild extends React.Component { 
  constructor(props){
   super(props);
   this.state = {
      somethingImp : [1,2,3,4]
   }
  }
  render() {
     return <button onClick = {this.props.handler(this.state.somethingImp)}>Clickme<button/>
  }
}
React.render(<Parent />,document.getElementById('app'));

 HTML
 ----
 <div id="app"></div>

この例では、直接の子に関数を渡すことで、SubChild-> Child-> Parentからデータを渡すことができます。


4

私はこのページから最高評価の回答を何度も使用しましたが、Reactの学習中に、バインディングを使用せず、プロップ内のインライン関数を使用せずに、より良い方法を見つけました。

ここを見てください:

class Parent extends React.Component {

  constructor() {
    super();
    this.state={
      someVar: value
    }
  }

  handleChange=(someValue)=>{
    this.setState({someVar: someValue})
  }

  render() {
    return <Child handler={this.handleChange} />
  }

}

export const Child = ({handler}) => {
  return <Button onClick={handler} />
}

キーはアロー関数にあります:

handleChange=(someValue)=>{
  this.setState({someVar: someValue})
}

詳しくはこちらをご覧ください。これが誰かに役立つことを願っています=)


これは私にとって非常に理にかなっています。ありがとうございました!
Brett Rowberry

3

-ParentComponentを作成し、handleInputChangeメソッドを使用してParentComponentの状態を更新できます。ChildComponentをインポートし、親から子コンポーネントに2つの小道具を渡します。つまり、handle.ChangeChange関数とカウントです。

import React, { Component } from 'react';
import ChildComponent from './ChildComponent';

class ParentComponent extends Component {
  constructor(props) {
    super(props);
    this.handleInputChange = this.handleInputChange.bind(this);
    this.state = {
      count: '',
    };
  }

  handleInputChange(e) {
    const { value, name } = e.target;
    this.setState({ [name]: value });
  }

  render() {
    const { count } = this.state;
    return (
      <ChildComponent count={count} handleInputChange={this.handleInputChange} />
    );
  }
}
  • 次に、ChildComponentファイルを作成し、ChildComponent.jsxとして保存します。子コンポーネントには状態がないため、このコンポーネントはステートレスです。小道具タイプのチェックには、小道具タイプライブラリを使用します。

    import React from 'react';
    import { func, number } from 'prop-types';
    
    const ChildComponent = ({ handleInputChange, count }) => (
      <input onChange={handleInputChange} value={count} name="count" />
    );
    
    ChildComponent.propTypes = {
      count: number,
      handleInputChange: func.isRequired,
    };
    
    ChildComponent.defaultProps = {
      count: 0,
    };
    
    export default ChildComponent;

子供が親の支柱に影響する子供を持っている場合、これはどのように機能しますか?
Bobort

3

この同じシナリオがどこにも広がっていない場合は、Reactのコンテキストを使用できます。特に、状態管理ライブラリがもたらすすべてのオーバーヘッドを導入したくない場合は特にそうです。さらに、習得も簡単です。しかし、注意してください、あなたはそれを使いすぎて、悪いコードを書き始めるかもしれません。基本的には、コンテナーコンポーネントを定義し(その状態の一部を保持し、保持します)、すべてのコンポーネントを、そのデータの一部の子(必ずしも直接の子ではない)の書き込み/読み取りに関心を持たせます。

https://reactjs.org/docs/context.html

代わりに、プレーンなReactを適切に使用することもできます。

<Component5 onSomethingHappenedIn5={this.props.doSomethingAbout5} />

doSomethingAbout5をコンポーネント1まで渡します

    <Component1>
        <Component2 onSomethingHappenedIn5={somethingAbout5 => this.setState({somethingAbout5})}/>
        <Component5 propThatDependsOn5={this.state.somethingAbout5}/>
    <Component1/>

これが一般的な問題である場合は、アプリケーションの状態全体を別の場所に移動することを検討する必要があります。いくつかのオプションがありますが、最も一般的なものは次のとおりです。

https://redux.js.org/

https://facebook.github.io/flux/

基本的に、コンポーネントでアプリケーションの状態を管理する代わりに、何かが起こって状態が更新されたときにコマンドを送信します。コンポーネントもこのコンテナから状態を取得するため、すべてのデータが一元化されます。これは、もはやローカル状態を使用できないという意味ではありませんが、それはより高度なトピックです。


2

したがって、親コンポーネントを更新する場合は、

 class ParentComponent extends React.Component {
        constructor(props){
            super(props);
            this.state = {
               page:0
            }
        }

        handler(val){
            console.log(val) // 1
        }

        render(){
          return (
              <ChildComponent onChange={this.handler} />
           )
       }
   }


class ChildComponent extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
             page:1
        };
    }

    someMethod = (page) => {
        this.setState({ page: page });
        this.props.onChange(page)
    }

    render() {
        return (
       <Button
            onClick={() => this.someMethod()} 
       > Click
        </Button>
      )
   }
}

ここで、onChangeは、そのインスタンスにバインドされた「ハンドラ」メソッドを持つ属性です。メソッドハンドラーを子クラスコンポーネントに渡し、props引数のonChangeプロパティを介して受け取ります。

属性onChangeは、次のようなpropsオブジェクトに設定されます。

props ={
onChange : this.handler
}

子コンポーネントに渡されます

したがって、Childコンポーネントは、このprops.onChangeのように、propsオブジェクトのnameの値にアクセスできます。

レンダープロップを使用して行われます。

これで、子コンポーネントのボタン「クリック」にonclickイベントが設定され、props引数オブジェクトのonChngeを介して渡されたハンドラーメソッドが呼び出されます。したがって、Child内のthis.props.onChangeは、ParentクラスReferenceとクレジット の出力メソッドを保持します Bits and Pieces


申し訳ありません。遅延のため、ここでonChangeは、そのインスタンスにバインドされた「ハンドラ」メソッドを持つ属性です。メソッドハンドラーを子クラスコンポーネントに渡し、props引数のonChangeプロパティを介して受け取ります。属性onChangeは、propsオブジェクトに次のように設定されます:props = {onChange:this.handler}子コンポーネントに渡されるため、子コンポーネントは、このprops.onChangeのようなpropsオブジェクトの名前の値にアクセスできます。レンダープロップの使用。 参考とクレジット: [ blog.bitsrc.io/...
Preetham NT

0

これが私のやり方です。

type ParentProps = {}
type ParentState = { someValue: number }
class Parent extends React.Component<ParentProps, ParentState> {
    constructor(props: ParentProps) {
        super(props)
        this.state = { someValue: 0 }

        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(value: number) {
        this.setState({...this.state, someValue: value})
    }

    render() {
        return <div>
            <Child changeFunction={this.handleChange} defaultValue={this.state.someValue} />
            <p>Value: {this.state.someValue}</p>
        </div>
    }
}

type ChildProps = { defaultValue: number, changeFunction: (value: number) => void}
type ChildState = { anotherValue: number }
class Child extends React.Component<ChildProps, ChildState> {
    constructor(props: ChildProps) {
        super(props)
        this.state = { anotherValue: this.props.defaultValue }

        this.handleChange = this.handleChange.bind(this)
    }

    handleChange(value: number) {
        this.setState({...this.state, anotherValue: value})
        this.props.changeFunction(value)
    }

    render() {
        return <div>
            <input onChange={event => this.handleChange(Number(event.target.value))} type='number' value={this.state.anotherValue}/>
        </div>
    }
}

0

上記の回答のほとんどは、React.Componentベースの設計に関するものです。useStateReactライブラリの最近のアップグレードで使用している場合は、この回答に従ってください


-3
<Footer 
  action={()=>this.setState({showChart: true})}
/>

<footer className="row">
    <button type="button" onClick={this.props.action}>Edit</button>
  {console.log(this.props)}
</footer>

Try this example to write inline setState, it avoids creating another function.

それはパフォーマンスの問題を引き起こします:stackoverflow.com/q/36677733/3328979
Arashsoft
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.