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

时间:2019-03-22 09:46:40

标签: javascript reactjs typescript transclusion

我正在寻找使用React进行开发的合适模式/方式,但是到目前为止,我没有发现任何相关的/优雅的方法。

我基本上想写一个表单引擎。对于表单的每个输入,我都希望有一些通用的行为。

我在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.dirtyFormInput组件中的标准行为,因此很显然,我不能像这样直接访问它们……

将已包含的内容连接到其容器组件的正确方法是什么?

编辑

仅是为了澄清和总结目标,我基本上想制作一个通用组件和一个包含在内的内容/模板。问题是我需要将特定的DOM模板(位于特定的组件中)绑定到通用处理程序(位于通用的组件中)。

预先感谢!

3 个答案:

答案 0 :(得分:0)

如果我理解正确,那么您的onChangedirty方法是在FormInput组件内部实现的。在这种情况下,input元素应放置在组件的render方法内,而不应作为子元素传递。

FormInput渲染方法应如下所示:

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

另外FormInput应该具有名称和类型属性才能以这种方式使用它:

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

我想,起初,您正试图传递与其他html元素包装在一起的输入的可能性。如果是这样的话,就我的建议而言,您只需包装新的FormInput即可获得相同的结果。

更新:

要在FormInput组件内渲染其他类型的表单元素,并且如果您对条件渲染不满意,可以尝试其他方法,在类型为prop的渲染内使用createElement方法。 / p>

更多信息在这里: Create Element

我以前使用过它,并且在我的项目中做得很好。

答案 1 :(得分:0)

根据您的评论:

  

FormInput必须托管所有标准内容,例如更改和验证。 TextInput基本上只能托管html <input>标签及其样式。

在我看来,您的等级制度是错误的。
我将创建一个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"/>

答案 2 :(得分:0)

我终于知道了!

这是我的解决方案:

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组件托管从状态到通用代码/行为(如dirtyonChange)的所有内容。

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

然后在FormInput中,我得到以下信息:

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

通用输入组件调用template函数,并传递呈现所需的内容。

这样,我将视图与行为分离了。

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