【问题标题】:React - Best practice with state in form components?React - 表单组件中状态的最佳实践?
【发布时间】:2016-06-05 03:53:35
【问题描述】:

我正在尝试了解有关 React 组件状态的最佳实践。我开始通过编写如下 TextField 组件来创建表单

var TextField = React.createClass({
    render: function() {
        const {value, title, placeholder} = this.props;
        return (<div>
            {title}
            <input type="text"
                value={value}
                placeholder={placeholder}
                onChange={this.handleChange} />
        </div>);
    },

    handleChange (evt){
        this.props.onChange(evt.target.value);
    }
});

这是一个受控组件。因此,父容器必须通过 props 为输入传递一个值,并在发生更改时更改该值。这似乎是通常的方法。

当我想创建一个数字字段时,我的问题就出现了。对于此示例,假设我的数字字段将允许输入非数字字符(该字段不会验证)。我不喜欢在父项中验证该字段的想法,所以这就是我写的

var NumericField = React.createClass({

    getInitialState: function(){
        return{
            value : ""
        }
    },

    componentWillReceiveProps: function(nextProps) {
        if(this.validate(nextProps.value)){
            this.setState({value:nextProps.value});
        }
    },

    validate : function(input) {
        return !isNaN(input);
    },

    render: function() {
        const {value} = this.state;
        const {title} = this.props;
        return (<div>
            {title}
            <input type="text"
                value={value}
                onChange={this.handleChange} />
        </div>);
    },

    handleChange (evt){
        this.setState({value:evt.target.value});
        if(this.validate(evt.target.value)){
            this.props.onChange(evt.target.value);
        }
    }
});

这允许父级设置值并通过“value”道具更新它,但“onChange”道具只会在内容有效时触发。对我来说,感觉就像我为每个人使用了不同的模式,这并不好。不确定这是否是一种有效的感觉?

我想我只是想问一下我对数字字段的处理是否合理,或者是否有更好的模式可以遵循?

以防万一有人想知道我为什么希望数字字段以这种方式工作,我不知道,这只是一个简化的示例。更有效的示例是 json 的文本区域,仅当内容为有效 json 时才调用 onChange。

感谢任何反馈

【问题讨论】:

    标签: javascript forms reactjs


    【解决方案1】:

    通常不赞成通过传入 props 来设置状态。 道具应该是不可变的(比如你的 NumericField 组件的标题) 如果你想设置一个初始值,它应该来自控制器或存储父组件从中获取它,例如。

    getInitialState() {
      return({
        value: FormDataStore.getInitialNumericFieldValue()
      });
    }

    之后,对值的任何更改都应由 NumericField 组件处理。如果您需要在设置新状态之前进行验证,例如。

    handleChange(evt) {
      if (this.validate(evt.target.value)){
        this.setState({
          value: evt.target.value
        });
        /* You can also pass the new validated value
           up to the parent component to hold on to
           till you're ready to process the form*/
        this.props.onChange(evt.target.value);
      }
    }

    您的状态现在将只保留(随后父组件将只接收)最后验证的值,因此如果 this.state.value === 输入,您甚至可以显示有效/无效消息,但这是额外的

    顺便说一下,您的 TextField 组件也应该遵循这种模式。将更改的值传递给父级只是为了让它再次作为道具传递下来,这违背了拥有子组件的目的。在这种情况下,我会将 JSX(和任何验证过程)全部放在父组件中,而不是抽象一个子组件。除非子组件可以重用。但是我还是让孩子处理自己的状态。

    【讨论】:

    • 感谢 X-Fix,这一切都说得通,我认为您关于 TextField 目的的权利有点毫无意义。如果整个表单的状态在树上发生变化,我希望我的表单组件能够更新。我一直在环顾四周,发现了这个用于表单构建的库mozilla-services.github.io/react-jsonschema-form,请注意如何在表单内和表单外更改表单数据。有什么想法可以实现吗?
    • @Ben 免责声明:我更熟悉将 Flux 与 React 结合使用(单向数据流)。我的方法是将数据保存在(例如)FormDataStore 中,然后来自第一个组件的 handleChange 函数将更改(成功验证后)直接保存到商店。使用 Flux,您可以使用动作创建者和调度程序,请参阅 link 。然后 store 发出一个 'change' 事件,第二个组件(表单外部)获取更新的值并更改以反映这一点,反之亦然,从表单外部到内部
    • @Ben 我还应该提到,通过这种方法,子组件不再存储/操作状态对象,并且可以通过父级传入的道具设置其字段值。然后,当商店发出“更改”事件时,父级会更新其数据
    • 谢谢,这听起来很合理。我查看了通量并认为它可能有助于提供更好的解决方案。我认为这是我接下来要探索的方向,只是想先整理一下我对 React 的基本知识。非常感谢您的帮助,谢谢。
    猜你喜欢
    • 2022-07-18
    • 1970-01-01
    • 1970-01-01
    • 2018-03-21
    • 1970-01-01
    • 2019-02-07
    • 1970-01-01
    • 2019-10-10
    • 1970-01-01
    相关资源
    最近更新 更多