在 React JS 中创建 <select> 元素

Posted

技术标签:

【中文标题】在 React JS 中创建 <select> 元素【英文标题】:Creating <select> elements in React JS 【发布时间】:2014-11-05 18:49:50 【问题描述】:

我正在用 react.js 为我的 web 应用程序重写 UI,但我有点被以下问题难住了。

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

现在,我想在表单中添加一个&lt;select&gt; 元素,并从不同的位置(url)获取值。

当前代码(没有&lt;select&gt;)看起来像这样(简化了一点,但所有工作细节都是一样的;它主要遵循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')
        );
    );

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

所以我想创建一个单独的React class,它只有getDataMixin,通过设置其props 的父级接收API url,并渲染&lt;select&gt; 元素;并在表单中使用此类。 但我不知道如何访问选定的值(因为父母无法访问它的孩子的refs)。 所以我需要另一种方法来“向上”传递所选值。

或者,如果这不可能,请朝正确的方向轻推——我不想最终得到大量不可重用的代码(我改用 react 的部分原因是使用 mixins并将代码保持在合理且可读的最低限度)。

【问题讨论】:

【参考方案1】:

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

但幸运的是,有一个非常简单的解决方案。你是对的,你会想要创建一个子组件来获取它自己的数据,而不是一个 mixin。但是,您如何处理将选定项目返回给父级的方式只是将父级的 onChange 函数传递给反应中的内置 &lt;select&gt;

以下是这种父子关系的示例:

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,以及任何其他对标准反应&lt;select&gt;元素有效的props。使用 react 内置的 this.transferPropsTo() 函数将所有道具转移到子渲染函数中的 &lt;select&gt; 元素。

这意味着定义changeHandlerMySelectonChange 处理程序的父级将此函数直接传递给子级的&lt;select&gt; 元素。 So when the select changes, the event gets handled by the parent instead of the child.顺便说一句,这是在 React 中做事的一种完全合法且合法的方式——因为它仍然遵循自上而下的理念。如果您考虑一下,当您使用来自 react 的普通内置 &lt;select&gt; 时,这正是正在发生的事情。 onChange 只是 &lt;select&gt; 的属性。

还值得注意的是,如果您愿意,您可以“劫持”更改处理程序以添加您自己的自定义逻辑。例如,我倾向于使用自定义输入组件包装我的 &lt;input type="text" /&gt; 字段,该组件接受任何和所有有效的 &lt;input&gt; 道具,但也将 validator 函数作为属性,它允许我定义一个返回 true 的函数/false 基于在&lt;input&gt; 字段中输入的值。我可以通过在父级中为包装子级定义 onChange 来做到这一点,但是在子级中我在内部定义自己的 onChange 来检查要定义的 this.props.onChange 和一个函数,然后运行我的验证器和通过调用this.props.onChange(e, valid) 将事件链传递回父级。在它的onChange 处理程序中为父级提供完全相同的事件对象,但还添加了一个布尔值valid 参数。反正我跑题了……

希望对你有所帮助:)

【讨论】:

Mike,正如您所写,您的解决方案不会在选择对象上调用渲染,因为您不使用 setState(x) 函数。我修改代码如下创建一个位置数组,var optionsArray = [];,然后更新该数组,最后在循环之后调用:this.setState(options:optionsArray); 很好——这就是我没有在答案中测试代码所得到的结果,尽管我个人更喜欢在直接改变状态对象时使用this.forceUpdate()。不过大多只是个人喜好。我已经编辑了我的答案以更新它。

以上是关于在 React JS 中创建 <select> 元素的主要内容,如果未能解决你的问题,请参考以下文章

在 React.js 中创建范围滑块

React.js。在函数体中创建带参数(和道具将是参数)或引用道具的方法更好吗?

如何在 react.js 中创建动态网格?

如何使用 React js JSX 在下拉列表中创建年份列表

如何在 React js 中创建状态数组

尝试在 React 中创建显示页脚,页脚忽略其他 div