在React JS中创建<select>元素</select>

时间:2014-09-11 17:46:01

标签: javascript jquery ajax html-form reactjs

我在react.js中为我的网络应用程序重写了用户界面,我对以下问题感到有些困惑。

我有一个显示通过AJAX请求获取的数据的页面,在下面显示了一个提交新数据的表单。一切都好。

现在,我想在表单中添加<select>元素,并从其他位置(网址)获取值。

当前代码(没有<select>)看起来像这样(简化了一下,但所有工作细节都相同;它主要遵循react.js网站上的教程):

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')
        );
    });

现在,我可以通过向select添加getDataMixin来添加TaskForm元素,获取数据并构建可能的选项列表,但我需要有多个表单列表,并且该方法似乎无法扩展(由于命名冲突;或者我需要使用除mixin之外的其他东西)。

所以我创建一个单独的React class,只有getDataMixin,通过父设置props接收API网址,然后渲染<select>元件;并在表单中使用此类。 但我不知道如何访问所选值(因为父母无法访问它的孩子的refs)。 所以我需要另一种方法来传递选定的值&#34; up&#34;。

或者,万一这是不可能的,一个正确方向的推动 - 我不想最终得到大量不可重复使用的代码(我之所以切换到反应的部分原因)是使用mixins并使代码保持理智和可读的最低限度。)

1 个答案:

答案 0 :(得分:17)

父母可以访问其子女的参考资料,只要您将孩子设置为父母的参考资料,您就可以访问该孩子的任何参考资料。例如this.refs.childRef.refs.nestedChildRef之类的东西。然而,这是一个非常糟糕的主意,除非你只是阅读信息(并处理错误,例如引用不正确,因为父母不知道孩子的引用是否设置正确)。

但幸运的是,有一个非常简单的解决方案。你是对的,你会想要创建一个子组件来获取它自己的数据,而不是mixin。但是你如何处理将所选项目交还给父项只是简单地从父项传递onChange函数,就像你在内部<select>反应一样。

以下是此父母子女关系的一个示例:

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>
        )
    }
});

上面的示例显示了一个父组件MyParent,其中包含一个子组件MySelect,它传递了一个url,以及对标准的反应<select>元素有效的任何其他道具。使用内置在this.transferPropsTo()函数中的react会将所有道具转移到其中{@ 1}}元素的渲染功能。

这意味着定义<select>作为changeHandler的{​​{1}}处理程序的父级将此函数直接传递给子级onChange元素。因此,当select更改时,事件由父级而不是子级处理。顺便说一句,这是在React做事的完美合法和合法的方式 - 因为它仍然遵循自上而下的哲学。如果你考虑一下,当你使用普通的内置MySelect做出反应时,这就是正在发生的事情。 <select>只是<select>的属性。

值得注意的是,如果你喜欢,你可以&#34; hyjack&#34;用于添加自己的自定义逻辑的更改处理程序。例如,我倾向于使用自定义输入组件包装我的onChange字段,该组件接受任何和所有有效的<select>道具,但也将<input type="text" />函数作为允许我使用的属性定义一个函数,该函数根据输入到<input>字段的值返回true / false。我可以通过在父项中为包装子项定义validator来做到这一点,但是在子项中我在内部定义了我自己的<input>,它检查要定义的onChange,以及一个函数,然后运行我的验证器并通过调用onChange将事件链传递回父级。为父提供相同的事件对象this.props.onChange处理程序,但也添加了布尔this.props.onChange(e, valid)参数。无论如何,我离题....

希望这可以帮助你:)