我正在寻找使用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.onChange
或this.dirty
是位于FormInput
组件中的标准行为,所以很明显,我无法直接访问它们......
将已转换内容连接到其容器组件的正确方法是什么?
编辑
为了澄清和总结目标,我基本上想要制作一个通用组件和一个被转换的内容/模板。这个问题是我需要将特定的DOM模板(在特定组件中)绑定到通用处理程序(它们在通用组件中)。
谢谢提前!
如果我理解正确,你的onChange
和dirty
方法是在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
我以前用它,并且在我的项目上工作得很好。
根据你的评论:
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"/>
我终于搞清楚了!
这是我的解决方案:
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
组件托管所有内容,从状态到公共代码/行为(如dirty
或onChange
)。
TextInput
组件仅实例化FormInput
并托管一个名为template
的函数,该函数使远程状态和通用FormInput
组件本身访问其函数。
然后在FormInput
,我有以下内容:
render() {
return this.props.template(this.state, this);
}
通用输入组件调用template
函数并传入渲染所需的内容。
这样,我就可以将视图与其行为分开。
希望你会喜欢它,以后它会帮助别人;)