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



我正在寻找合适的模式/方法来使用 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 模板(在特定组件中)绑定到泛型处理程序(在泛型组件中)。

提前感谢!

如果我理解正确,您的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 组件中呈现其他类型的表单元素,如果您对条件呈现不满意,可以尝试其他方式,在渲染中使用 yout type propcreateElement方法。

更多信息在这里: 创建元素

我以前用过它,在我的项目中工作得很好。

根据您的评论:

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

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

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

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

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

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

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

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

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

希望你会喜欢它,并且它会帮助以后的人;)

最新更新