web-dev-qa-db-ja.com

React JSで<select>要素を作成する

React.jsでWebアプリのUIを書き換えていますが、次の問題に少し困惑しています。

AJAXリクエストで取得したデータを表示するページがあり、その下に新しいデータを送信するフォームが表示されます。

ここで、フォームに<select>要素を追加し、別の場所(url)から値を取得します。

現在のコード(<select>なし)は次のようになります(少し簡略化されていますが、作業の詳細はすべて同じです。ほとんどは、react.js Webサイトのチュートリアルに従います)。

var tasks_link = $('#tasks_link');

var getDataMixin = {
        loadDataFromServer: function() {
            $.ajax({
                url: this.props.url,
                dataType: 'json',
                success: function(data) {
                    this.setState({data: data});
                }.bind(this),
                error: function(xhr, status, err) {
                    console.error(this.props.url, status, err.toString());
                }.bind(this)
            });
        },
        getInitialState: function() {
            return {data: []};
        },
        componentDidMount: function() {
            this.loadDataFromServer();
        }
    };

var sendDataMixin = {
        handleDataSubmit: function(senddata) {
            $.ajax({
                url: this.props.url,
                dataType: 'json',
                contentType: 'application/json',
                type: 'POST',
                data: senddata,
                success: function(data) {
                    var curr_d = this.state.data;
                    var curr_d_new = curr_d.concat([data]);
                    this.setState({data: curr_d_new});
                }.bind(this),
                error: function(xhr, status, err) {
                    console.error(this.props.url, status, err.toString());
                }.bind(this)
            });
        }
    };

var taskForm = React.createClass({
        handleSubmit: function() {
            var name = this.refs.task_name.getDOMNode().value.trim();
            if (!name) {
              return false;
            }
            this.props.onTaskSubmit(JSON.stringify({name: name}));
            this.refs.task_name.getDOMNode().value = '';
            return false;
        },
        render: function () {
            return (
                <form className="well base_well new_task_well" onSubmit={this.handleSubmit}>
                    <div className="form-group">
                        <div className="input-group">
                            <span className="input-group-addon no_radius">Task name</span>
                            <input type="text" className="form-control no_radius" id="add_new_project_input" ref="task_name"/>
                        </div>
                    </div>
                    <button type="button" className="btn btn-default no_radius add_button" id="add_new_task_btn" type="submit">Add task</button>
                </form>
            );
        }
    });

var taskBox = React.createClass({
        mixins: [getDataMixin, sendDataMixin],
        render: function () {
            return (
                <div id="project_box" className="taskBox"> <taskList data={this.state.data} />
                    <taskForm onTaskSubmit={this.handleDataSubmit}/> </div>
            );
        }
    });

tasks_link.click(function() {
        React.renderComponent(
            <taskBox url="/api/tasks/" />,
            document.getElementById('content_container')
        );
    });

selectgetDataMixinに追加してデータを取得し、可能なオプションのリストを作成することで、TaskForm要素を追加できますが、多くのフォームを作成する必要がありますリスト、およびアプローチは(名前の衝突のために、または私はミックスイン以外のものを使用する必要があります)スケーリングしないようです。

だから私は別のReact classを作成しますが、これはgetDataMixinを持つだけで、propsを設定する親を介してAPI urlを受け取り、<select>要素をレンダリングします;フォーム内でこのクラスを使用します。しかし、選択した値にアクセスする方法がわかりません(親は子のrefsにアクセスできないため)。そのため、選択した値を「上」に渡す別の方法が必要です。

または、これが不可能な場合、正しい方向へのナッジ–再利用できない大量のコードになりたくありません(反応に切り替えた理由の一部は、ミックスインを使用して健全で読みやすい最小限のコード)。

13

子を親の内部参照として設定している限り、親はその子の参照にアクセスできます。その子内部の参照にアクセスできます。たとえば、_this.refs.childRef.refs.nestedChildRef_のようなものです。しかし、これは、情報を読んでいるだけでない限り、本当に悪い考えです(そして、親は子のrefが正しく設定されているかどうかわからないため、refが正しく存在しないなどのエラーを処理します)。

しかし、幸いなことに非常に簡単な解決策があります。そうです、mixinではなく、独自のデータを取得する子コンポーネントを作成する必要があります。ただし、選択したアイテムを親に返す処理は、reactの組み込み_<select>_に行うように、親からonChange関数を渡すだけです。

この親子関係の例を次に示します。

_var MyParent = React.createClass({
    getInitialState: function() {
        return {
            childSelectValue: undefined
        }
    },
    changeHandler: function(e) {
        this.setState({
            childSelectValue: e.target.value
        })
    },
    render: function() {
        return (
            <div>
                <MySelect 
                    url="http://foo.bar"
                    value={this.state.childSelectValue}
                    onChange={this.changeHandler} 
                />
            </div>
        )
    }
});

var MySelect = React.createClass({
    propTypes: {
        url: React.PropTypes.string.isRequired
    },
    getInitialState: function() {
        return {
            options: []
        }
    },
    componentDidMount: function() {
        // get your data
        $.ajax({
            url: this.props.url,
            success: this.successHandler
        })
    },
    successHandler: function(data) {
        // assuming data is an array of {name: "foo", value: "bar"}
        for (var i = 0; i < data.length; i++) {
            var option = data[i];
            this.state.options.Push(
                <option key={i} value={option.value}>{option.name}</option>
            );
        }
        this.forceUpdate();
    },
    render: function() {
        return this.transferPropsTo(
            <select>{this.state.options}</select>
        )
    }
});
_

上記の例は、標準のリアクション_<select>_要素に有効な他のプロップとともに、URLを渡す子コンポーネントMyParentを含む親コンポーネントMySelectを示しています。反応の組み込みthis.transferPropsTo()関数を使用して、すべての小道具をそのrender関数の子の_<select>_要素に転送します。

これは、changeHandleronChangeハンドラとしてMySelectを定義する親が、この関数を子の_<select>_要素に直接渡すことを意味します。したがって、選択が変更されると、イベントは子ではなく親によって処理されます。ちなみに、これはReact-まだトップダウンの哲学に従っているので、通常のビルトインを使用すると、 _<select>_は、reactから、まさにこれが起こっていることですonChangeは、単に_<select>_のプロパティです。

また、必要に応じて、変更ハンドラを「ハイジャック」して独自のカスタムロジックを追加できることにも注意してください。たとえば、私がしがちなのは、すべての有効な_<input type="text" />_ propsを受け取るカスタム入力コンポーネントで_<input>_フィールドをラップするだけでなく、validator関数をプロパティとして取り込んで、 _<input>_フィールドに入力された値に基づいてtrue/falseを返す関数を定義します。ラッパーの子に対して親でonChangeを定義することでこれを行うことができますが、子では、定義される_this.props.onChange_をチェックする独自のonChangeを内部で定義します。関数、次にバリデーターを実行し、this.props.onChange(e, valid)を呼び出してイベントチェーンを親に戻します。親にonChangeハンドラーでまったく同じイベントオブジェクトを与えますが、ブール値valid引数も追加します。とにかく、私は脱線します....

これがあなたを助けることを願っています:)

17
Mike Driver