ReactJS简单表单向导

时间:2017-05-18 20:45:58

标签: javascript forms validation reactjs redux

出于学习目的,我正在使用ReactJS中的向导编写一个小应用程序。

我已经为向导创建了一些组件,如下所示;

  • 进度
  • 向导
    • WizardStepOne
    • WizardStepOneForm
    • WizardStepTwo
    • WizardStepTwoForm

在向导组件中,我已经包含了我的PorgressBar组件以显示进度,并创建了一个switch语句来确定每个' StepForm'中包含的按钮的oneClick值。获取一个值并显示下一个“WizardStepTwoForm”#39;成分

这一切都运作良好,完全符合我的预期,但我面临一个问题。我不希望用户能够获得下一个WizardStepTwoForm'在我验证了WizardStepOneForm之前的表格。所以我应该如何将状态返回到我的父组件以确定用户是否可以单击到下一个状态。或者,我按状态禁用按钮,直到验证完成,但在这种情况下,用户无法点击按钮来验证表单。

在提交表单期间我想将数据发送到API,调度正在运行,但我只是想知道如何解决这个问题,所以我的父语言(向导)中的switch语句只有在表格有效。

向导

//.. imports

class Wizard extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            step : 'stepOne',
            progression : '0%'
        };

        this.handleFormSubmit = this.handleFormSubmit.bind(this);
    }

    componentDidMount() {
        const wizard = JSON.parse(localStorage.getItem('wizard'));
        if (wizard !== null) {
            this.setState({
                step: wizard.step,
                progression: wizard.progression
            });
        }
    }

    componentDidUpdate() {
        localStorage.setItem('wizard', JSON.stringify({
            step        : this.state.step,
            progression : this.state.progression
        }));
    }

    handleFormSubmit(e) {

        switch (e.target.value) {
            case 'stepOne' :
                this.setState({
                    step : 'stepOne',
                    progression : '0%',
                });
                break;
            case 'stepTwo' :
                this.setState({
                    step : 'stepTwo',
                    progression : '50%'
                });
                break;
        }
    }

    /**
     *
     * Render
     * @return {JSX}
     */
    render() {

        const { step, progression } = this.state;

        switch (step) {
            case 'stepOne' :
                return (
                    <div>
                        <Header />

                            <WizardProgressBar progression={progression} stepOne="active" stepTwo="" />

                            <WizardStepOne handleFormSubmit={this.handleFormSubmit} />

                        <Footer/>
                    </div>
                );
                break;
            case 'stepTwo' :
                return (
                    <div>
                        <Header />

                            <WizardProgressBar progression={progression} stepOne="done" stepTwo="active" />

                            <WizardStepTwo handleFormSubmit={this.handleFormSubmit} />

                        <Footer/>
                    </div>
                );
                break;
        }
    }
}

export default Wizard;

WizardStepOne

export default class WizardStepOne extends React.Component {

constructor(props) {
    super(props);
}

/**
 *
 * Render
 * @return {XML}
 */
render() {

    return(
        <div className="step-one">

            <h1>Step 1</h1>

            <WizardStepOneForm handleFormSubmit={this.props.handleFormSubmit} />

        </div>
    );
}

}

WizardStepForm

//... imports

@connect((store) => {
    return {

    };
})

export default class WizardStepOneForm extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            formData : {
                firstName : '',
                lastName  : '',
            },
            formErrors : {
                firstName : true,
                lastName  : true,
            },
            formErrorMessages : {
                firstName : 'some validation message',
                lastName  : 'some validation message',
            },
            formButtonEnabled : true,
        }

        this.handleSubmit          = this.handleSubmit.bind(this);
        this.handleFirstNameChange = this.handleFirstNameChange.bind(this);
        this.handleLastNameChange  = this.handleLastNameChange.bind(this);
    }

    componentDidMount() {
        const stepOne = JSON.parse(localStorage.getItem('stepOne'));
        if (stepOne !== null) {
            this.setState({
                formData : stepOne.formData,
                formErrors : stepOne.formErrors,
                formErrorMessages : stepOne.formErrorMessages,
            });
        }
    }

    handleFirstNameChange(e) {

        let formData          = this.state.formData;
        let formErrors        = this.state.formErrors;
        let formErrorMessages = this.state.formErrorMessages;

        formData.firstName    = e.target.value;

        if (!e.target.value) {
            formErrors.firstName = true;
        } else {
            formErrors.firstName = false;
        }
        this.setState({ formData : formData, formErrors : formErrors, formErrorMessages : formErrorMessages });
        localStorage.setItem('stepOne', JSON.stringify({ formData : formData, formErrors : formErrors, formErrorMessages : formErrorMessages }));
    }

    handleLastNameChange(e) {

        let formData          = this.state.formData;
        let formErrors        = this.state.formErrors;
        let formErrorMessages = this.state.formErrorMessages;

        formData.lastName     = e.target.value;

        if (!e.target.value) {
            formErrors.lastName = true;
        } else {
            formErrors.lastName = false;
        }
        this.setState({ formData : formData, formErrors : formErrors, formErrorMessages : formErrorMessages });
        localStorage.setItem('stepOne', JSON.stringify({ formData : formData, formErrors : formErrors, formErrorMessages : formErrorMessages }));
    }

    handleSubmitButton() {

    }

    handleSubmit(e) {

        e.preventDefault();

        this.props.dispatch(addUser(this.state.formData));
    }

    /**
     *
     * Render
     * @return {XML}
     */
    render() {

        const firstNameError = this.state.formErrors.firstName ? 'error' : '';
        const lastNameError  = this.state.formErrors.lastName  ? 'error' : '';

        return(
            <form className="step-one-form">

                <div className="form-group right">

                    <div className="form-group__form-row">

                        <p className={classnames('col-2', firstNameError)}>
                            <label htmlFor="first_name">First name:</label>
                            <input type="text" id="firstName" name="fist_name" autoComplete="off" onChange={this.handleFirstNameChange} value={this.state.formData.firstName} />
                            { firstNameError ? <FormElementErrorMessage message={this.state.formErrorMessages.firstName} /> : '' }
                        </p>

                        <p className={classnames('col-2', lastNameError)}>
                            <label htmlFor="last_name">Last name:</label>
                            <input type="text" id="lastName" name="last_name" autoComplete="off" onChange={this.handleLastNameChange} value={this.state.formData.lastName} />
                            { lastNameError ? <FormElementErrorMessage message={this.state.formErrorMessages.lastName} /> : '' }
                        </p>

                    </div>

                </div>

                <button disabled={this.state.formButtonEnabled} onClick={this.props.handleFormSubmit} value="stepTwo">Next step</button>

            </form>
        );
    }
}

1 个答案:

答案 0 :(得分:0)

所以,我发现它可能比我想象的更容易。

表单提交处理程序位于创建表单的子组件中。所以我们不需要从父母那里把它作为支柱向下推。

当表单'stepOne'有效时,我们将通过调度程序触发一个动作,我们通过reducer更改给定的状态。商店收到此状态更改,已通过连接和应用程序根目录中的提供程序附加,父组件将接收此状态更改,我们可以启动下一个向导屏幕。