代码之家  ›  专栏  ›  技术社区  ›  jrkt

ReactJS访问渲染组件的道具

  •  3
  • jrkt  · 技术社区  · 6 年前

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

    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 按钮以验证该步骤中的信息并移动到下一步。我只是想把这个按钮放在每个步骤的组件中,但这会让它变得不那么友好。当用户单击“下一步”,并且所有内容都有效时,该步骤应折叠,下一步应打开。然而,这意味着 工作流 组件需要渲染此按钮。

    所以,我需要 工作流 组件调用每个步骤组件的方法来验证步骤中的信息,并返回一个承诺,让它知道是否通过或失败(带有任何错误消息)。我需要如何调用此方法?这里是 工作流 组件渲染所有步骤 像 <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>
                ...
            );
        })
    }
    

    这将呈现next按钮以及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 回复  |  直到 6 年前
        1
  •  1
  •   Ross Allen    6 年前

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

    它可能如下所示:

    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 源代码,它工作得很好。