2016-03-21 86 views
6

私はReactを初めて使うので、データを兄弟コンポーネント間でやりとりする必要があるタスクをどのように達成するのが最適かについて質問したいと思います。React.js - 兄弟コンポーネント間の通信

まず、私はタスクを説明します:

私は、アレイから成る動的に選択ボックスを下に渡し、単一の親の子である複数の<select>のコンポーネントを持っていると言います。各ボックスは初期状態でまったく同じ利用可能なオプションを持っていますが、ユーザがあるボックスで特定のオプションを選択すると、それが解放されるまで他のすべてのボックスでオプションとして無効にする必要があります。

ここでは、同じ(愚かな)コードの例を示します。

この例では、「これは私のお気に入りです」と「それは私の一番お気に入り」のオプションを無効にする必要があります(つまりdisabled: true)。ユーザーは1つの選択ボックスでそれらを選択します(ユーザーが選択を解除した場合は解放します)。

var React = require('react'); 
 
var Select = require('react-select'); 
 

 

 

 
var AnForm = React.createClass({ 
 

 
    render: function(){ 
 

 

 
     // this.props.fruits is an array passed in that looks like: 
 
     // ['apples', 'bananas', 'cherries','watermelon','oranges'] 
 
     var selects = this.props.fruits.map(function(fruit, i) { 
 

 
      var options = [ 
 
       { value: 'first', label: 'It\'s my favorite', disabled: false }, 
 
       { value: 'second', label: 'I\'m OK with it', disabled: false }, 
 
       { value: 'third', label: 'It\'s my least favorite', disabled: false } 
 
      ]; 
 

 

 
      return (
 
       <Child fruit={fruit} key={i} options={options} /> 
 
      ); 
 
     }); 
 

 

 
     return (
 
      <div id="myFormThingy"> 
 
       {fruitSelects} 
 
      </div> 
 
     ) 
 
    } 
 

 
}); 
 

 

 
var AnChild = React.createClass({ 
 

 
    getInitialState: function() { 
 
     return { 
 
      value:'', 
 
      options: this.props.options 
 
     }; 
 
    }, 
 

 
    render: function(){ 
 

 
     function changeValue(value){ 
 
      this.setState({value:value}); 
 
     } 
 

 

 
     return (
 
      <label for={this.props.fruit}>{this.props.fruit}</label> 
 
      <Select 
 
       name={this.props.fruit} 
 
       value={this.state.value} 
 
       options={this.state.options} 
 
       onChange={changeValue.bind(this)} 
 
       placeholder="Choose one" 
 
      /> 
 
     ) 
 
    } 
 
});

最高のコールバックを介して親にバックアップデータを渡すことによって達成子オプションを更新していますか?そのコールバック内の子コンポーネントにアクセスするためにrefを使用する必要がありますか? redux reducerは助けてくれますか?

私はこの質問の一般的な性質についてお詫びしますが、これらの兄弟と兄弟のコンポーネントのやりとりを一方向の方法で処理する方法については、多くの方向性を見出していません。

ありがとうございました。

+0

はい、はい、はいの違いがあります。 reduxは、グローバル変数を使用するのと同じように、コンポーネントのロジックの観点から基本的に何をしているのかを簡単にします。 – dandavis

答えて

19

TLDR:はい、トップからボトムまでのプロップと、ハンドラからボトム・ツー・トップのアプローチを使用する必要があります。しかし、これは大規模なアプリケーションで扱いにくくなる可能性があるため、FluxやReduxなどのデザインパターンを使用して複雑さを軽減することができます。単純

コンポーネントが小道具としての「入力」を受信反応させたアプローチを反応させます。彼らは小道具としてそれらに渡された関数を呼び出すことによって、 "出力"を伝達します。標準的な例:

<input value={value} onChange={changeHandler}> 

初期値を1つの小道具に渡します。別の小道具にチェンジハンドラがあります。

値を渡してハンドラをコンポーネントに変更できるのは誰ですか?その親だけ。 (例外はありますが、コンテキストを使用してコンポーネント間で情報を共有できますが、それはより高度な概念です。次の例で活用されます)

したがって、あなたの選択のための入力を管理する必要があります。次に例を示します。

class Example extends React.Component { 

    constructor(props) { 
     super(props); 
     this.state = { 
      // keep track of what is selected in each select 
      selected: [ null, null, null ] 
     }; 
    } 

    changeValue(index, value) { 
     // update selected option 
     this.setState({ selected: this.state.selected.map((v, i) => i === index ? value : v)}) 
    } 

    getOptionList(index) { 
     // return a list of options, with anything selected in the other controls disabled 
     return this.props.options.map(({value, label}) => { 
      const selectedIndex = this.state.selected.indexOf(value); 
      const disabled = selectedIndex >= 0 && selectedIndex !== index; 
      return {value, label, disabled}; 
     }); 
    } 

    render() { 
     return (<div> 
      <Select value={this.state.selected[0]} options={this.getOptionList(0)} onChange={v => this.changeValue(0, v)} /> 
      <Select value={this.state.selected[1]} options={this.getOptionList(1)} onChange={v => this.changeValue(1, v)} /> 
      <Select value={this.state.selected[2]} options={this.getOptionList(2)} onChange={v => this.changeValue(2, v)} /> 
     </div>) 
    } 

} 

Reduxの

上記のアプローチの主な欠点は、あなたが上から下に多くの情報を渡す必要があるということです。アプリケーションが大きくなると、これを管理するのが難しくなります。React-ReduxはReactのコンテキスト機能を活用して、子コンポーネントが直接Storeにアクセスできるようにすることで、アーキテクチャを簡素化します。

例(あなたのReduxのアプリケーションのほんのいくつかの重要な部分 - 例えばCREATESTORE、プロバイダ、一緒にこれらの配線方法に反応-Reduxのドキュメントを参照してください...):

// reducer.js 

// Your Store is made of two reducers: 
// 'dropdowns' manages the current state of your three dropdown; 
// 'options' manages the list of available options. 

const dropdowns = (state = [null, null, null], action = {}) => { 
    switch (action.type) { 
     case 'CHANGE_DROPDOWN_VALUE': 
      return state.map((v, i) => i === action.index ? action.value : v); 
     default: 
      return state; 
    } 
}; 

const options = (state = [], action = {}) => { 
    // reducer code for option list omitted for sake of simplicity 
}; 

// actionCreators.js 

export const changeDropdownValue = (index, value) => ({ 
    type: 'CHANGE_DROPDOWN_VALUE', 
    index, 
    value 
}); 

// helpers.js 

export const selectOptionsForDropdown = (state, index) => { 
    return state.options.map(({value, label}) => { 
     const selectedIndex = state.dropdowns.indexOf(value); 
     const disabled = selectedIndex >= 0 && selectedIndex !== index; 
     return {value, label, disabled}; 
    });  
}; 

// components.js 

import React from 'react'; 
import { connect } from 'react-redux'; 
import { changeDropdownValue } from './actionCreators'; 
import { selectOptionsForDropdown } from './helpers'; 
import { Select } from './myOtherComponents'; 

const mapStateToProps = (state, ownProps) => ({ 
    value: state.dropdowns[ownProps.index], 
    options: selectOptionsForDropdown(state, ownProps.index) 
}}; 

const mapDispatchToProps = (dispatch, ownProps) => ({ 
    onChange: value => dispatch(changeDropdownValue(ownProps.index, value)); 
}); 

const ConnectedSelect = connect(mapStateToProps, mapDispatchToProps)(Select); 

export const Example =() => (
    <div> 
     <ConnectedSelect index={0} /> 
     <ConnectedSelect index={1} /> 
     <ConnectedSelect index={2} /> 
    </div> 
); 

あなたが見ることができるように、ロジックReduxの例ではバニラのReactコードと同じです。しかし、それは親コンポーネントに含まれていませんが、レデューサーとヘルパー関数(セレクター)に含まれています。 React-Reduxは、トップダウンで小道具を渡す代わりに、個々のコンポーネントを状態に結びつけるので、よりシンプルでモジュール化され、保守が容易なコードになります。

+1

本当に役立つ例と違いの説明。一緒に入れていただきありがとうございます。今のところ、私はバニラの反作用のソリューションに行きました。これは治療のように働いています。 – JMcClure

+1

最初の1〜2週間でReduxは必須と思っています。この定数を渡すコールバックは、正式なものですが、エレガントな解決策ではありません。今Reduxを学ぶ必要があるかもしれません。 – user798719

2

以下は、2人の兄弟の間の通信をセットアップするのに役立ちます。設定は、render()およびcomponentDidMount()の呼び出し中に親で行われます。

class App extends React.Component<IAppProps, IAppState> { 
    private _navigationPanel: NavigationPanel; 
    private _mapPanel: MapPanel; 

    constructor() { 
     super(); 
     this.state = {}; 
    } 

    // `componentDidMount()` is called by ReactJS after `render()` 
    componentDidMount() { 
     // Pass _mapPanel to _navigationPanel 
     // It will allow _navigationPanel to call _mapPanel directly 
     this._navigationPanel.setMapPanel(this._mapPanel); 
    } 

    render() { 
     return (
      <div id="appDiv" style={divStyle}> 
       // `ref=` helps to get reference to a child during rendering 
       <NavigationPanel ref={(child) => { this._navigationPanel = child; }} /> 
       <MapPanel ref={(child) => { this._mapPanel = child; }} /> 
      </div> 
     ); 
    } 
} 
関連する問題