ReactJS访问渲染组件的道具

时间:2018-05-14 13:07:25

标签: reactjs state

我正在构建一个将用于步进过程的组件,例如:

enter image description here

Workflow组件将array个“步骤”作为道具,然后完成剩下的工作。以下是上图中调用它的方式:

let steps = [
{
    display: "Sign Up Form",
    component: SignupForm
},
{
   display: "Verify Phone",
   component: VerifyPhone
},
{
   display: "Use Case Survey",
   component: UseCase
},
{
   display: "User Profile",
   component: UserProfile
},
];

return (
    <Workflow
        steps={steps}
    />
);

component字段指向要在该步骤中呈现的组件。例如,SignupForm组件如下所示:

export default class SignupForm extends React.Component {
    ...
    render() {
        return (
            <div>
                <div className="page-header">
                    <h1>New User Sign Up Form</h1>
                    <p>Something here...</p>
                </div>

                <div className="form-group">
                    <input type="email" className="form-control" placeholder="Email address..." />
                    <small id="emailHelp" className="form-text text-muted">We'll never share your email with anyone else.</small>
                </div>
            </div>
        );
    }
}

我面临的问题是,每一步都需要一个Next按钮来验证该步骤中的信息并转到下一步。我只是将该按钮放在每个步骤的组件中,但这使得它对用户不太友好。当用户单击“下一步”并且一切都有效时,应该折叠该步骤并打开下一步。但是,这意味着我的Workflow组件需要呈现此按钮。

因此,我需要我的Workflow组件来调用每个步骤组件的方法来验证步骤中的信息并返回一个承诺,让它知道它是否通过或失败(带有任何错误消息)。我该如何调用此方法?这是Workflow组件呈现所有步骤的位置  作为<step.component {...this.props} />

{
    this.state.steps.map((step, key) => {
        return (
            ...
                <Collapse isOpen={!step.collapsed}>
                    <step.component {...this.props} />
                    <Button color="primary"
                            onClick={() => this.validate(key)}>Next</Button>
                    <div className="invalid-value">
                        {step.error}
                    </div>
                </Collapse>
            ...
        );
    })
}

呈现下一个按钮,以及onClick处理程序validate()

    validate(i) {
        let steps = _.cloneDeep(this.state.steps);
        let step = steps[i];

        step.component.handleNext().then(function () {
            ...
        }).catch((err) => {
            ...
        });
    }

理想情况下,step.component.validate()会在已经呈现的组件内调用validate方法:

export default class SignupForm extends React.Component {
    ....

    validate() {
        return new Promise((resolve, reject) => {
            resolve();
        })
    }

    render() {
        ...
    }
}

..可以访问该组件的状态。但是,这不是它的工作原理。我怎样才能让它发挥作用?我读了一些关于转发引用的内容,但不确定它是如何工作的。非常感谢任何帮助!

1 个答案:

答案 0 :(得分:1)

一种方法是通过使您的表单成为上下文Observer pattern并使其提供&#34;注册表来应用Provider。注册Consumers的功能。您的消费者将是每个XXXForm组件。它们都会实现相同的validate API,因此包装表单可以假定它可以在任何已注册的组件上调用validate

它可能如下所示:

const WorkflowContext = React.createContext({
  deregisterForm() {},
  registerForm() {},
});

export default class Workflow extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      forms: [],
    };
  }

  deregisterForm = (form) => {
    this.setState({
      forms: this.state.forms.slice().splice(
        this.state.forms.indexOf(forms), 1)
    });
  };

  registerForm = (form) => {
    this.setState({ forms: [ ...this.state.forms, form ] })
  };

  validate = () => {
    const validationPromises = this.state.forms.reduce(
      (promises, formComponent) => [...promises, formComponent.validate()]);
    Promise.all(validationPromises)
      .then(() => {
        // All validation Promises resolved, now do some work.
      })
      .catch(() => {
        // Some validation Promises rejected. Handle error.
      });
  };

  render() {
    return (
      <WorkflowContext.Provider
        value={{
          deregisterForm: this.deregisterForm,
          registerForm: this.registerForm,
        }}>
        {/* Render all of the steps like in your pasted code */}
        <button onClick={this.validate}>Next</button
      </WorkflowContext.Provider>
    );
  }
}

// Higher-order component for giving access to the Workflow's context
export function withWorkflow(Component) {
  return function ManagedForm(props) {
    return (
      <WorkflowContext.Consumer>
        {options =>
          <Component
            {...props}
            deregisterForm={options.deregisterForm}
            registerForm={options.registerForm}
          />
        }
      </WorkflowContext.Consumer>
    );
  }
}

SignupForm以及需要实施验证的任何其他表单:

import { withWorkflow } from './Workflow';

class SignupForm extends React.Component {
  componentDidMount() {
    this.props.registerForm(this);
  }

  componentWillUnmount() {
    this.props.deregisterForm(this);
  }

  validate() {
    return new Promise((resolve, reject) => {
      resolve();
    })
  }

  render() {
    ...
  }
}

// Register each of your forms with the Workflow by using the
// higher-order component created above.
export default withWorkflow(SignupForm);

我最初发现这种模式在阅读react-form's源时应用于React,并且效果很好。

相关问题