【问题标题】:ReactJS Accessing Props of Rendered ComponentReactJS 访问渲染组件的 props
【发布时间】:2018-10-24 03:50:03
【问题描述】:

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

这个Workflow 组件将'steps' 的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 组件呈现所有步骤的地方 如&lt;step.component {...this.props} /&gt;:

{
    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() {
        ...
    }
}

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

【问题讨论】:

    标签: reactjs state


    【解决方案1】:

    一种方法是应用Observer pattern,方法是使您的表单成为上下文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,并且效果很好。

    【讨论】:

    • FormManager 是我的Workflow 组件的实现吗?还是Workflow组件的实现?
    • @JonStevens 抱歉,我意识到我没有使用您原来的组件名称。我只是用你的名字重写了代码。
    • 不用担心。 Workflow 组件的实现看起来如何?我在尝试使用 Workflow 组件的组件中遇到错误。它的使用位置:return ( &lt;Workflow steps={steps} /&gt; ); .. 我收到一条错误消息:Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: object.
    • 它在我上面问题的第一个代码 sn-p 中。这更容易阅读。
    • 哦,我的代码在render 方法中也缺少return,哈哈。抱歉,我实际上并没有运行此代码...
    猜你喜欢
    • 2018-03-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-07-20
    • 2023-04-10
    • 1970-01-01
    • 2020-07-31
    • 1970-01-01
    相关资源
    最近更新 更多