React JS:可重用组件

React JS: Reusable components

本文关键字:组件 JS React      更新时间:2023-09-26

我已经用类似的结构创建了表单验证

var Signin = React.createClass({
    render: function() {
        return (
                <Form>
                    <Input type="text" name="email" labelName="Email" rules="isEmail" error:"Email not valid" />
                    <Input type="password" name="password" labelName="Password" rules="isLength:6" error:"Passowrd not valid"/>
                </Form>
        );
    }
});

例如,因为"电子邮件"输入将在应用程序的不同部分使用,我会避免每次添加相同的属性(名称、类型、labelName、规则和错误)。所以我会创建这样的

var InputEmail = React.createClass({
    render: function () {
        return (
            <Input type="text" name="email" labelName="Email" rules="isEmail" error="Email not valid"/>
        )
    }
});
var InputPassword = React.createClass({
    render: function () {
        return (
            <Input type="password" name="password" labelName="Password" rules="isLength:6" error="Passwordnot valid"/>
        )
    }
});

所以登录组件应该是

var Signin = React.createClass({
    render: function() {
        return (
                <Form>
                    <InputEmail />
                    <InputPassword />
                </Form>
        );
    }
});

但是这样,我得到了两个错误:

  1. 我在表单中找不到Input的props.name,因为InputEmail中没有;

  2. 在Input的呈现函数中,状态为空

如何创建可重用/继承的组件?我使用合成模式和混合都失败了

我添加了我的完整代码:Form

var Form = React.createClass({
    getInitialState: function () {
        return {
            isValid : false,
            isSubmitting: false
        }
    },
    componentWillMount: function(){
        this.model = {};
        this.inputs = {};
        this.registerInputs(this.props.children);
    },
    registerInputs: function(children){

        React.Children.forEach(children, function (child) {

            if (child.props.name) {

                child.props.attachToForm = this.attachToForm;
                child.props.detachFromForm = this.detachFromForm;
                child.props.validate = this.validate;
            }
            if (child.props.children) {
                this.registerInputs(child.props.children);
            }
        }.bind(this));
    },

    attachToForm: function (component) {
        this.inputs[component.props.name] = component;
        this.model[component.props.name] = component.state.value;
        this.validate(component);
    },
    detachFromForm: function (component) {
        delete this.inputs[component.props.name];
        delete this.model[component.props.name];
    },
    validate: function (component) {
        var isValid = true;
                // validation code
        component.setState({
            isValid: isValid,
        }, this.validateForm);
    },

    validateForm: function () {
        var formIsValid = true;
        var inputs = this.inputs;
        Object.keys(inputs).forEach(function (name) {
            if (!inputs[name].state.isValid) {
                formIsValid = false;
            }
        });
        this.setState({
            isValid: formIsValid
        });
    },
    updateModel: function (component) {
        Object.keys(this.inputs).forEach(function (name) {
            this.model[name] = this.inputs[name].state.value;
        }.bind(this));
    },
    submit: function (event) {
        event.preventDefault();
        this.setState({
            isSubmitting : true
        });
        this.updateModel();
        console.log(this.model);
    },
    render: function () {
        return (
            <form className="ui form" onSubmit={this.submit}>
                {this.props.children}
                <button className="ui button" type="submit" disabled={this.state.isSubmitting}>Accedi</button>
            </form>
        );
    }
});

输入

var Input = React.createClass({
    getInitialState: function(){
        return {
            value : this.props.value || "",
            isValid: true
        }
    },
    setValue: function (event) {
        this.setState({
            value: event.target.value
        }, function () {
            this.props.validate(this);
        }.bind(this));
    },
    componentWillMount: function () {
        if (this.props.required) {
            this.props.validations = this.props.validations ? this.props.validations + ',' : '';
            this.props.validations += 'isLength:1';
        }
                // ERROR: TypeError: this.props.attachToForm is not a function
        this.props.attachToForm(this);
    },
    componentWillUnmount: function () {
        this.props.detachFromForm(this);
    },

    render: function () {
        var className = "field";
        if(this.props.className){
            className +=  " " + this.props.className;
        }
        if(this.props.required){
            className += " required";
        }
        var Label;
        if(this.props.labelName){
            Label = (<label htmlFor={this.props.name}>{this.props.labelName}</label>);
        }
        var Error;
        if(!this.state.isValid){
            Error = (<div className="ui">{this.props.error || this.props.name + " not valid"}</div>);
        };

        return (
            <div className={className}>
                {Label}
                <input type={this.props.type || "text"} id={this.props.name} name={this.props.name} onChange={this.setValue} value={this.state.value} />
                {Error}
            </div>
        );
    }
});

有了这个作品

ReactDOM.render(
    <Form>
        <Input type="text" name="email" labelName="Email" rules="isEmail" error:"Email not valid"/>
    </Form>,
    document.getElementById('app')
);

通过这种方式,我得到:"TypeError:this.props.attachToForm不是函数this.props.attachToForm(this);"

ReactDOM.render(
    <Form>
        <InputEmail/>
    </Form>,
    document.getElementById('app')
);

p.S:我试图在jsfiddle上添加此代码,但我得到了"!TypeError:无法定义属性"attachToForm":对象不可扩展"

jsfiddle

您的设置有两个主要问题:

  1. 您的<Form>是以这样一种方式设置的,即表单的子级需要有道具,否则它将不起作用
  2. <InputEmail>包装不完整。它需要将所有道具传递给<Input>,包括传递下来的Form函数

广告1:修复表单,以确保添加验证方法
出现错误的原因是<Form>的子级需要有props.name。然后,它通过将函数添加到子级来注册表单的函数(包括attachToForm)。这是在方法registerInputs()中完成的。

在最初的变体中,<Input>组件有道具,所以一切都很顺利。

在适配的变体中,包装器<InputEmail>不再具有props,因此attachToForm()和其他函数不会添加到props中,并且当<Input>尝试调用该函数时会出现错误。

最简单的方法是:在渲染函数中添加至少一个道具,并在registerInputs()中检查,例如:

ReactDOM.render(
    <Form>
        <InputEmail initialValue={'name@domain.com'}/>
    </Form>,
    document.getElementById('app')
);

registerInputs()中,更改行:

if (child.props.name) {

至:

if (child.props.initialValue) {

2.扩展<InputEmail>包装器以传递函数
最简单的方法是添加{...this.props},如下所示:

var InputEmail = React.createClass({
    render: function () {
        return (
            <Input {...this.props}
              type="text" name="email" labelName="Email" rules="isEmail" error="Email not valid"/>
        )
    }
});

这样,<Form>传递给<InputEmail>组件的函数(以及任何其他道具)将传递给<Input>组件。

PS:registerInputs()中检查child.props.children的代码没有按预期工作:在调用它时,<InputEmail>组件没有子级。顾名思义,它检查作为道具流传下来的孩子。唯一通过的道具是initialValue

作为次要问题,我建议再做两个更改:

  1. registerInputs()中,您可以直接修改道具。这通常不是一个好主意。最好是制作一个道具副本,并将你的成型方法添加到副本中。您可以使用React.Children.map来执行此操作。请参阅此处的官方文档
  2. 与其在<InputEmail>中对<Input>组件的name="email"等进行硬编码,不如使用propTypes将这些组件的默认值放在道具的默认值中,如官方文档中所述