反应包含和与容器组件的通信

React transclusion and communication with container component

我正在寻找合适的 pattern/way 来使用 React 开发它,但到目前为止我没有找到任何东西 relevant/elegant。

主要是想写一个表单引擎。对于表单的每个输入,我想要一些通用行为。

我在 React 文档中读到继承不是正确的方式;好的方法是设计一个通用组件并通过组合对其进行专门化。

在我的顶级组件中,我想写这样的东西:

<Form>
    <TextInput .../>
    <TextInput .../>
    <EmailInput .../>
</Form>

每种类型的输入基本上必须始终做相同的事情:示例:根据其验证器等检查此值

因此,我设计了一个包含所有这些标准行为的通用组件 FormInput。当我编写 TextInput 组件时,它看起来像这样:

export default class TextInput extends React.Component {

    constructor(props) {
        super(props);
    }

    render() {
        return (
            <FormInput>
                <input name={this.props.name} 
                       type='text'
                       onChange={this.onChange}
                       onBlur={this.dirty}
                       value={this.state.value}
                />
            </FormInput>
        );
    }

}

现在,问题是 this.onChangethis.dirty 是位于 FormInput 组件中的标准行为,所以显然,我不能那样直接访问它们...

将嵌入的内容连接到其容器组件的正确方法是什么?

编辑

只是为了明确和总结目标,我基本上想做一个通用组件和一个嵌入content/template。这个问题是我需要将特定的 DOM 模板(在特定组件中)绑定到通用处理程序(在通用组件中)。

提前致谢!

如果我没理解错的话,您的 onChangedirty 方法是在 FormInput 组件中实现的。在这种情况下,input 元素应该放在组件的渲染方法中,而不是作为子元素传递。

FormInput 渲染方法应该是这样的:

render(){
    return  <input name={this.props.name} 
                       type={this.props.type}
                       onChange={this.onChange}
                       onBlur={this.dirty}
                       value={this.state.value}
                />
}

另外 FormInput 应该有 name 和 type 属性以这样使用它:

 <FormInput name='A name' type='text' />

我想,最初你试图传递一个包含其他 html 元素的输入的可能性。如果是这样的话,根据我的建议,您只需包装新的 FormInput 即可获得相同的结果。

更新:

要在 FormInput 组件中渲染其他类型的表单元素,如果您对条件渲染不满意,您可以尝试其他方式,在渲染中使用 createElement 方法和你的类型 prop .

更多信息在这里: Create Element

我以前用过它,在我的项目中效果很好。

根据您的评论:

FormInput must host all the standard things like changes and validations. TextInput must basically only host the html <input> tag and its style.

在我看来你的层次结构似乎是错误的。
我会创建一个 Input 来负责原始 input 的外观,并创建一个 Form 来负责嵌套的 input 的行为方式以及它们保存的数据。

这是一个 运行 示例,显然您可以使用其他方式设置样式而不是内联样式。

const Input = ({ label, ...rest }) => (
  <div style={{ display: "flex" }}>
    <div style={{ display: "flex", alignItems: "center", margin: "10px 0" }}>
      <label style={{ minWidth: "100px", margin: "0 10px" }}>{label}</label>
      <input style={{ padding: "0.5em" }} {...rest} />
    </div>
  </div>
);

class Form extends React.Component {
  state = {
    fName: "",
    lName: "",
    age: null
  };

  onChange = e => {
    const { name, value } = e.target;
    this.setState({ [name]: value });
  };

  onSubmit = e => {
    e.preventDefault();
    const { onSubmit } = this.props;
    onSubmit(this.state);
  };

  render() {
    const { fName, lName, age } = this.state;
    return (
      <form onSubmit={this.onSubmit}>
        <Input
          type="text"
          name="fName"
          label="First Name"
          value={fName}
          required
          onChange={this.onChange}
        />
        <Input
          type="text"
          name="lName"
          label="Last Name"
          value={lName}
          required
          onChange={this.onChange}
        />
        <Input
          type="number"
          name="age"
          label="Age"
          value={age}
          required
          onChange={this.onChange}
        />
        <button type="submit">Submit</button>
      </form>
    );
  }
}

function App() {
  return (
    <div>
      <Form onSubmit={data => console.log(data)} />
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"/>

我终于想通了!

这是我的解决方案:

export default class TextInput extends React.Component {

    constructor(props) {
        super(props);
    }

    template(state, formInput) {
        return (
             <input name={this.props.name} 
                    type='text'
                    onChange={formInput.onChange}
                    onBlur={formInput.dirty}
                    value={state.value}/>
        );
    }

    render() {
        return (
            <FormInput {...this.props} template={this.template} />
        );
    }

}

终于比预想的要简单,但是我没有使用嵌入

FormInput 组件托管所有内容,从状态到通用 code/behaviors(如 dirtyonChange)。

TextInput 组件仅实例化一个 FormInput 并托管一个名为 template 的函数,该函数采用远程状态和通用 FormInput 组件本身来访问其函数.

然后在 FormInput 中,我得到以下内容:

render() {
    return this.props.template(this.state, this);
}

通用输入组件调用template函数并传入渲染所需的内容。

通过这种方式,我将视图与其行为分离。

希望你会喜欢它,它会对以后的人有所帮助 ;)