簡體   English   中英

React JS:可重用組件

[英]React JS: Reusable components

我已經用這樣的結構創建了表單驗證

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. 我無法在Form中找到Input的props.name,因為InputEmail中沒有。

  2. 在Input的呈現函數中,狀態為null

如何創建可重用/繼承的組件? 我無法同時使用合成模式和mixins

我添加了完整的代碼:表格

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

PS:我試圖在jsfiddle上添加此代碼,但得到“!TypeError:無法定義屬性“ attachToForm”:對象不可擴展”

的jsfiddle

設置有兩個主要問題:

  1. 您的<Form>是以這樣的方式設置的,即該表單的子級需要有道具,否則它將不起作用。
  2. <InputEmail>包裝器不完整。 它需要將所有道具傳遞到<Input> ,包括向下傳遞的Form函數。

廣告1:修正表格,以確保添加了驗證方法
出現錯誤的原因是<Form>子代需要具有props.name。 然后,通過將窗體的功能(包括attachToForm)添加到子級中來注冊它們。 這是在方法registerInputs()

在原始變體中, <Input>組件具有道具,因此一切順利。

在改編的變體中,包裝器<InputEmail>不再具有道具,因此, attachToForm()和其他函數不會添加到道具,並且當<Input>嘗試調用該函數時會出現錯誤。

解決此問題的最簡單方法:在render函數中添加至少1個prop,然后在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

作為次要問題,我建議再進行2處更改:

  1. registerInputs() ,您可以直接修改道具。 這通常不是一個好主意。 更好的方法是制作道具副本,然后將形式方法添加到副本中。 您可以使用React.Children.map來做到這一點。 在此處查看官方文檔
  2. 代替在<InputEmail>內對<Input>組件的name="email"等進行硬編碼,更好的方法是使用propTypes將其默認值放入props的默認值中,如官方文檔中所述

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM