本系列文章是本人学习相关知识时所积累的笔记,以记录自己的学习历程,也为了方便回顾知识;故文章内容较为随意简练,抱着学习目的来的同学务必转移他处,以免我误人子弟~
工作中遇到个需求,有个表单字段的值是由数组构成,而且数组的长度不确定。于是我想着使用 React 封装个表单组件,可以实现添加任何子表单组件并且输出数组值,如:
<DynamicFieldComponent> <Input /> </DynamicFieldComponent>
输出的值则可以是
["字符串1","字符串2","字符串3","字符串4",...]
如果子表单组件是 Select
<DynamicFieldComponent> <Select multiple={false}> <Select.option>选项1</Select.option> <Select.option>选项2</Select.option> <Select.option>选项3</Select.option> </Select> </DynamicFieldComponent>
输出的值则可以是
["选项1","选项2","选项1","选项3",...]
问题
在调试阶段,我先在 DynamicFieldComponent 组件中写死了子表单组件,即不能接收任意的表单组件,并在写死的表单组件上绑定了事件处理函数:
// DynamicFieldComponent
...
...
handleChange(){
...
}
render(){
let items = this.state.keys.map((key,index)=>{
return <Input onChange={this.handleChange} key={key} />;
})
return(
{items}
)
}
现在 DynamicFieldComponent 中,我是用 this.props.children
来接收子表单组件的,这时我无法直接将事件处理函数绑定在 this.props.children 上面。
解决
其实在 React 中,提供了一些 API 供我们使用,这里我们需要使用的两个api是React.Children.only
和React.cloneElement
React.Children.only
用来处理this.props.children
,只接受仅一个元素的this.props.children
,返回React.ReactElement
数据类型React.cloneElement
第一个参数接收类型为React.ReactElement
的元素,第二个参数接收该元素的期待的属性值
所以我先用 React.Children.only
将 React.ReactNode
转为 React.ReactElement
,再使用 React.cloneElement
克隆出一个新的元素,并同时指定事件处理函数,问题就得到解决了。
// DynamicFieldComponent
...
...
handleChange(){
...
}
render(){
const child = React.Children.only(this.props.children);
const items = this.state.keys.map((key: number, index: number) => {
const childElement = React.cloneElement(child, {
onChange: (value: any) => this.handleChange(key, value),
});
return childElement;
}
return(
{items}
)
}
最后放上这个组件的完整代码:github:zyhcool
参考资料
官方文档:React.cloneElement
简书:[React] this.props.children和React.cloneElement