1. ホーム
  2. reactjs

[解決済み】フォーム要素の状態を兄弟/親要素に渡す正しい方法とは?

2022-04-28 23:21:09

質問

  • ReactのクラスPがあり、C1とC2という2つの子クラスをレンダリングしているとします。
  • C1には入力フィールドがあります。この入力フィールドをFooと呼ぶことにする。
  • 私の目標は、Fooの変更にC2が反応するようにすることです。

2つの解決策を思いついたのですが、どちらもいまいちしっくりきません。

第一の解決策

  1. Pに状態を割り当てる。 state.input .
  2. を作成します。 onChange この関数は、イベントを取り込んで state.input .
  3. これを渡す onChange としてC1へ渡す。 props を、C1 にバインドさせます。 this.props.onChangeonChange をFooの

これは動作します。Fooの値が変わるたびに、その値をトリガーとして setState を生成し、P は C2 に渡す入力を得ることができます。

しかし、同じ理由で、子要素から親要素の状態を設定するのは、どうもしっくりこない。これは、Reactの設計方針である「単一方向のデータフロー」を裏切っているように思えます。

それとも、もっとReactらしい解決策があるのでしょうか?

第二の解決策

PにFooを入れればいいんです。

しかし、これはアプリを構成する際に従うべき設計原則なのでしょうか。すべてのフォーム要素を render は、最上位クラスのものですか?

この例のように、C1を大量にレンダリングする場合、すべてのC1ファイルに render のC1を render を、C1がform要素を持っているというだけで、Pの

どうすればいいのでしょうか?

どのように解決するのですか?

つまり、私が正しく理解していれば、最初の解決策は、ルート・コンポーネントに状態を保持していることを示唆しているのですね?私はReactの制作者について語ることはできませんが、一般的に、これは適切な解決策だと思います。

状態の維持は、Reactが作られた理由のひとつです(少なくとも私はそう考えています)。もしあなたが、相互に依存する多くの可動部分を持つ動的なUIを扱うために、クライアント側で独自のステートパターンを実装したことがあるなら、Reactを好きになるでしょう。

ルートコンポーネントのイベントに応答しているだけで、双方向バインディングでデータを取得しているわけではありません。ルートコンポーネントに「ちょっと、ここで何か起きたから値を調べてみて」と伝えるか、子コンポーネントのデータの状態を渡して状態を更新しているのです。C1で状態を変更し、C2にそれを認識させたい場合、ルートコンポーネントで状態を更新して再レンダリングすると、ルートコンポーネントで状態が更新されて渡されたため、C2のプロップは同期されます。

class Example extends React.Component {
  constructor (props) {
    super(props)
    this.state = { data: 'test' }
  }
  render () {
    return (
      <div>
        <C1 onUpdate={this.onUpdate.bind(this)}/>
        <C2 data={this.state.data}/>
      </div>
    )
  }
  onUpdate (data) { this.setState({ data }) }
}

class C1 extends React.Component {
    render () {
      return (
        <div>
          <input type='text' ref='myInput'/>
          <input type='button' onClick={this.update.bind(this)} value='Update C2'/>
        </div>
      )
    }
    update () {
      this.props.onUpdate(this.refs.myInput.getDOMNode().value)
    }
})

class C2 extends React.Component {
    render () {
      return <div>{this.props.data}</div>
    }
})

ReactDOM.renderComponent(<Example/>, document.body)