反应与容器组件的转换和通信

问题描述 投票:0回答:3

我正在寻找使用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.dirty是位于FormInput组件中的标准行为,所以很明显,我无法直接访问它们......

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

编辑

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

谢谢提前!

javascript reactjs typescript transclusion
3个回答
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组件中呈现其他类型的表单元素,并且如果您对条件渲染感觉不舒服,可以尝试其他方式,使用类型道具在渲染中使用createElement方法。

更多信息:Create Element

我以前用它,并且在我的项目上工作得很好。


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"/>

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组件本身访问其函数。

然后在FormInput,我有以下内容:

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

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

这样,我就可以将视图与其行为分开。

希望你会喜欢它,以后它会帮助别人;)

© www.soinside.com 2019 - 2024. All rights reserved.