javascript - React 中的多步骤形式

标签 javascript reactjs office-ui-fabric

我想在 Microsoft UI Fabric 的支持下在 React 中创建一个多步骤表单。

类似的事情:

Multistep Form mockup

因此,我想创建一个动态多步骤表单,其中内部组件完全相互独立(除非存在一些显式依赖关系)。 我想完成这样的事情:

public render(): React.ReactElement<MyComponentProps> {

    const list = [
      { headerText: 'Personal Details', component: <PersonalDetailsTab /> },
      { headerText: 'Company details', component: <CompanyDetailsTab /> },
      { headerText: 'Other', component: <OtherTab /> }
    ];

    return (
      <MultiStepForm steps={list} ></MultiStepForm>
    );
}

这会很棒。

请注意,内部的三个组件彼此不认识。 此外:

  • 状态管理
  • UI 呈现
  • 验证

...完全独立

但是,当我想要管理组件之间的状态和验证时,我当然会遇到问题。 主要是:

  • 当我单击选项卡中的“下一个”按钮并转到下一个选项卡,然后我想通过单击“上一个”按钮再次返回到上一个选项卡时,我丢失了字段的数据之前已填写。

  • 下一个选项卡还必须能够依赖于前一个选项卡中输入的数据(例如,在第三个选项卡中,调用传递姓氏和公司 ID 的服务)。我想这样就会失去独立性和脱钩性……

  • 当我从一个选项卡中单击“NEXT”并转到下一个选项卡时,我相信应该有一种机制,其中状态必须尊重某些约束(愚蠢的示例:第二个选项卡不接受以前的任何人)以“Rossi”姓氏命名...第二个选项卡(或第一个选项卡?)应该显示验证错误)。

遗憾的是我不是 React 专家。可能我不想使用 Redux 或其他框架。

这是我的解决方案:

MyComponents.tsx:

export default class MyComponent extends React.Component<MyComponent> {

    constructor(props: MyComponent) {
    super(props);

    this.state = {};
    }

    public render(): React.ReactElement<MyComponentProps> {
        const list = [
          { headerText: 'Personal Details', component: <PersonalDetailsTab /> },
          { headerText: 'Company details', component: <CompanyDetailsTab /> },
          { headerText: 'Other', component: <OtherTab /> }
        ];

        return (
            <MultiStepForm steps={list} ></MultiStepForm>
        );
    }
}

MultiStepForm.tsx:

import * as React from 'react';
import { Pivot, PivotItem, PivotLinkSize, PivotLinkFormat } from 'office-ui-fabric-react';
import { StepTab } from '../StepTab';

interface IMultiStepFormProps {
    steps: any[];
}

interface IMultiStepFormState {
    steps: any;
    stepsCount: number;
    selectedKey: number;
}

export class MultiStepForm extends React.Component<IMultiStepFormProps, IMultiStepFormState> {

    constructor(props: IMultiStepFormProps) {
        super(props);

        this._handleBackClick = this._handleBackClick.bind(this);
        this._handleNextClick = this._handleNextClick.bind(this);
        this._onLinkClick = this._onLinkClick.bind(this);
        this._updateComponent = this._updateComponent.bind(this);

        this.state = {
            selectedKey: 0,
            steps: props.steps,
            stepsCount: props.steps.length
        };
    }

    public render(): React.ReactElement<IMultiStepFormProps> {
        return (
            <Pivot linkSize={PivotLinkSize.large} linkFormat={PivotLinkFormat.tabs}
                selectedKey={`${this.state.selectedKey}`}
                onLinkClick={this._onLinkClick} >
                {this.state.steps.map((s, i) =>
                    <PivotItem headerText={s.headerText} itemKey={`${i}`} >
                        <StepTab
                            component={s.component} componentState={s.childrenState}
                            _handleBackClick={this._handleBackClick}
                            _handleNextClick={this._handleNextClick}
                            _updateComponent={this._updateComponent}
                        />
                    </PivotItem>)}
            </Pivot>
        );
    }


    private _updateComponent(comp, compState) {
        // work in progress...
        var s = this.state.steps;
        s[this.state.selectedKey].component = comp;
        s[this.state.selectedKey].childrenState = compState;
        this.setState({
            steps: s
        });
    }

    private _handleBackClick(): void {
        let currentIndex = this.state.selectedKey;
        let count = this.state.stepsCount;
        let newIndex = ((currentIndex - 1) + count) % count;
        this.setState({
            selectedKey: newIndex
        });
    }
    private _handleNextClick(): void {
        let currentIndex = this.state.selectedKey;
        let count = this.state.stepsCount;
        let newIndex = (currentIndex + 1) % count;
        this.setState({
            selectedKey: newIndex
        });
    }
    private _onLinkClick(item: PivotItem): void {
        let newIndex = parseInt(item.props.itemKey);
        this.setState({
            selectedKey: newIndex
        });
    }

}

StepTab.tsx:

import * as React from 'react';
import { PrimaryButton } from 'office-ui-fabric-react';

export interface IStepProps {
    component: React.Component;
    componentState?: any;
    _handleBackClick: () => void;
    _handleNextClick: () => void;
    _updateComponent: (comp: any, compState: any) => void;
}
export interface IStepState {
    componentState: React.Component;
    // _getComponentState: (x, y) => void;
}
export class StepTab extends React.Component<IStepProps, IStepState> {
    constructor(props) {
        super(props);

        this.handleInputChange = this.handleInputChange.bind(this);

        this.state = {
            componentState: this.props.componentState || {},
            // _getComponentState: this.props._updateComponent
        };
    }

    public componentDidMount() {
        console.log("StepTab MOUNT");
        if (this.state.componentState) {
            // work in progress

        }
    }

    public render(): React.ReactElement<IStepProps> {
        return (
            <form onSubmit={this._onSubmit.bind(this)} onChange={this.handleInputChange} >
                {this.props.component}
                <div style={{ textAlign: "right" }}>
                    <PrimaryButton text="BACK" onClick={(this._onBack.bind(this))} allowDisabledFocus={true} />
                    <PrimaryButton text="NEXT" type="submit"
                        allowDisabledFocus={true} />
                </div>
            </form>
        );
    }
    public _onBack(e) {
        e.preventDefault();
        this.props._handleBackClick();
    }
    public _onSubmit(e) {
        e.preventDefault();
        this.props._updateComponent(this.props.component, this.state.componentState);
        this.props._handleNextClick();
    }
    public handleInputChange(event) {
        const target = event.target;
        const value = target.type === 'checkbox' ? target.checked : target.value;
        const name = target.name || target.id;
        var partialState = this.state.componentState;
        partialState[name] = value;
        this.setState({ componentState: partialState });
    }
}

ExamplesTabs.tsx:

import * as React from 'react';
import {
    TextField, ITextFieldProps, Stack, IconButton, ComboBox, Label, MaskedTextField,
    Rating, IComboBoxOption, SelectableOptionMenuItemType, getId
} from 'office-ui-fabric-react';

export class PersonalDetailsTab extends React.Component<{}, {}> {
    public render(): React.ReactElement<{}> {
        return (<>
            <TextField name="name" label="Name"></TextField>
            <TextField name="surname" label="Surname"></TextField>
            {/* checkbox */}
        </>);
    }
}

export class CompanyDetailsTab extends React.Component<{}, {}> {
    public render(): React.ReactElement<{}> {
        return (<>
            <TextField name="companyId" label="Company id" ></TextField>
            <TextField name="companyName" label="Company Name"></TextField>
        </>);
    }
}

export class OtherTab extends React.Component<{}, {}> {
    public render(): React.ReactElement<{}> {
        return (<>
            {/* bla bla */}
        </>);
    }
}

如果有人有一些建议或有效的替代方案:)

最佳答案

这是 Brad https://github.com/bradtraversy/react_step_form 的好例子, https://www.youtube.com/watch?v=zT62eVxShsY - 指南

我有一个具有相同方法的多步骤形式的沙箱​​项目,mb它与您想要的类似: https://github.com/AleksK1NG/React-meetuper/blob/master/client/src/components/MeetupCreate/MeetupCreateWizard/MeetupCreateWizard.js (MeetupCreate文件夹是多步骤形式)

希望您理解多步骤表单功能的主要思想:)

关于javascript - React 中的多步骤形式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57231082/

相关文章:

javascript - NodeJS/express : server hanged after start running for a while

reactjs - 如何将文本输入返回到 React 组件

javascript - 第二次单击时 office-fabric-ui 中的上下文菜单问题

office-ui-fabric - 在 UI 中心显示 Spinner

javascript - 更改标签内的文本 onclick

javascript - Javascript 每五秒循环一次

python - 通过 Flask-Python 为 React 生产应用程序(本地服务器)提供服务

javascript - console.log 是否调用对象的 toString 方法?

JSON 响应的 JavaScript 百分比计算