一.表单实现功能:
①Form 组件可以被 ref 获取。然后可以调用实例方法 submitForm 获取表单内容,用于提交表单,resetForm 方法用于重置表单。
②Form组件自动过滤掉除了FormItem之外的其他React元素
③FormItem 中 name 属性作为表单提交时候的 key ,还有展示的 label
④ FormItem 可以自动收集 <Input/> 表单的值。
二.form编写
类组件中,父组件可以通过ref获取子组件的实例,但在函数组件中,父组件没办法通过ref直接拿到子组件的方法和状态,因此需要对ref进行转发:
- forwardRef、useImperativeHandle实现的子组件方法转发。
为了辨别form传入组件的身份
- 给函数组件或者类组件绑定静态属性来证明它的身份。
利用isValidElement判断元素类别,进行form表单的元素进行过滤
- React.isValidElement验证元素类别。
利用React.Children.map方式进行react元素遍历,React.Children.clone对元素进行隐式注入 props
- React.Children.map 、 React.Children.clone,实现了可控render。
import React, { useState, forwardRef, useImperativeHandle, useRef } from 'react'
// /* Input 组件, 负责回传value值 */
function Input({ onChange, value }) {
return <input className="input"
onChange={(e) => (onChange && onChange(e.target.value))}
value={value}
/>
}
// /* 给Component 增加标签 */
Input.__COMPONENT_TYPE = 'input'
function FormItem(props) {
const { children, name, handleChange, value = '', label } = props
const onChange = (value) => {
/* 通知上一次value 已经改变 */
handleChange(name, value)
}
return <div className="form" >
<span className="label" >{label}:</span>
{
React.isValidElement(children) && children.type.__COMPONENT_TYPE === 'input'
? React.cloneElement(children, { onChange, value })
: null
}
</div>
}
FormItem.__COMPONENT_TYPE = 'formItem'
const Form = forwardRef((props, ref) => {
const [formData, setFormData] = useState({})
// 提交表单
const submitForm = (callback) => {
callback(formData)
}
// 重置表单
const resetForm = () => {
const resetVale = {}
Object.keys(formData).forEach(item => {
resetVale[item] = ''
})
setFormData(resetVale)
};
// 设置表单数据层
const setValue = (name, value) => {
setFormData({
...formData,
[name]: value
})
};
// 将子组件中的方法暴露给
useImperativeHandle(ref, () => ({
submitForm,
resetForm,
setValue
}))
return React.Children.map(props?.children, (child) => {
if (child.type.__COMPONENT_TYPE === 'formItem') {
const { name } = child.props
const Children = React.cloneElement(child, {
key: name,
handleChange: setValue,
value: formData[name]
})
return Children
}
})
})
Form.__COMPONENT_TYPE = 'form'
export default () => {
const form = useRef(null)
const submit = () => {
/* 表单提交 */
form.current.submitForm((formValue) => {
console.log(formValue)
})
}
const reset = () => {
/* 表单重置 */
form.current.resetForm()
}
return <div className="box" >
<Form ref={form}>
<FormItem label="我是"
name="name"
>
<Input />
</FormItem>
<FormItem label="我想对大家说"
name="mes"
>
<Input />
</FormItem>
<input placeholder="不需要的input" />
<Input />
</Form>
<div className="btns" >
<button className="searchbtn"
onClick={submit}
>提交</button>
<button className="concellbtn"
onClick={reset}
>重置</button>
</div>
</div>
}