反应包含和与容器组件的通信
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.onChange
或 this.dirty
是位于 FormInput
组件中的标准行为,所以显然,我不能那样直接访问它们...
将嵌入的内容连接到其容器组件的正确方法是什么?
编辑
只是为了明确和总结目标,我基本上想做一个通用组件和一个嵌入content/template。这个问题是我需要将特定的 DOM 模板(在特定组件中)绑定到通用处理程序(在通用组件中)。
提前致谢!
如果我没理解错的话,您的 onChange
和 dirty
方法是在 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(如 dirty
或 onChange
)。
TextInput
组件仅实例化一个 FormInput
并托管一个名为 template
的函数,该函数采用远程状态和通用 FormInput
组件本身来访问其函数.
然后在 FormInput
中,我得到以下内容:
render() {
return this.props.template(this.state, this);
}
通用输入组件调用template
函数并传入渲染所需的内容。
通过这种方式,我将视图与其行为分离。
希望你会喜欢它,它会对以后的人有所帮助 ;)
我正在寻找合适的 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.onChange
或 this.dirty
是位于 FormInput
组件中的标准行为,所以显然,我不能那样直接访问它们...
将嵌入的内容连接到其容器组件的正确方法是什么?
编辑
只是为了明确和总结目标,我基本上想做一个通用组件和一个嵌入content/template。这个问题是我需要将特定的 DOM 模板(在特定组件中)绑定到通用处理程序(在通用组件中)。
提前致谢!
如果我没理解错的话,您的 onChange
和 dirty
方法是在 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(如 dirty
或 onChange
)。
TextInput
组件仅实例化一个 FormInput
并托管一个名为 template
的函数,该函数采用远程状态和通用 FormInput
组件本身来访问其函数.
然后在 FormInput
中,我得到以下内容:
render() {
return this.props.template(this.state, this);
}
通用输入组件调用template
函数并传入渲染所需的内容。
通过这种方式,我将视图与其行为分离。
希望你会喜欢它,它会对以后的人有所帮助 ;)