表单是html的基础元素,接下来我会用React实现一个表单组件。支持包括输入状态管理,表单验证,错误信息展示,表单提交,动态表单元素等功能。
数据状态
表单元素的输入状态管理,可以基于react state 实现。
const [formData, setFormData] = useState(initial_data);
参数校验
在表单元素变更后,对变更结果进行验证,若验证失败,则更新失败状态,若验证成功,则更新数据状态, 并移除之前老的失败状态。
/**
* 表单错误状态
*/
const [errors, setErrors] = useState({});
/**
* 表单数据变更处理函数
*/
const setFieldData = (name, value) => {
// 进行参数校验
if (validators && validators[name]) {
const error = validators[name](value);
if (error) {
setErrors((errors) => ({...errors, [name]: error}));
return;
}
setErrors((errors) => {
const newErrors = {...errors};
delete newErrors[name];
return newErrors;
})
}
// 更新表单数据
setFormData({
...formData,
[name]: value
});
}
表单提交
表单提交需要判断是否有校验失败错误,如果有的话提交失败,如果没有提交成功。
/**
* 表单提交处理函数
*/
const handleSubmit = (e) => {
e.preventDefault();
if (errors && Object.keys(errors).length > 0) {
console.log('表单校验未通过');
return;
}
if (submitFunc) {
console.log('开始执行提交函数');
submitFunc(formData);
}
}
表单项组件
表单项组件会根据参数不同的类型返回不同的组件,并且error和fieldData,setFieldData与父组件Form绑定。
/**
* 表单项组件
*/
const FormItem = ({name, type, error, label, fieldData, setFieldData}) => {
if (type === 'submit') {
return (
<div>
<input type="submit" value={label}/>
</div>
)
} else if (type === 'text') {
return (
<div>
<label htmlFor={name}>{label}</label>
<input type="text" name={name} value={fieldData} onChange={e => setFieldData(name, e.target.value)}/>
{error && <span>{error}</span>}
</div>
)
} else if (type === 'password') {
return (
<div>
<label htmlFor={name}>{label}</label>
<input type="password" name={name} value={fieldData}
onChange={e => setFieldData(name, e.target.value)}/>
{error && <span>{error}</span>}
</div>)
}
return null;
}
组件整体代码
Form组件是基于React实现,并对表单form的功能进行日常封装。
import {useState} from "react";
/**
* 表单组件
* @param initial_data 初始数据
* @param validators 校验器
* @param submitFunc 提交函数
* @param children FormItem组件列表
*/
const Form = ({initial_data, validators, submitFunc, children}) => {
/**
* 表单数据状态
*/
const [formData, setFormData] = useState(initial_data);
/**
* 表单错误状态
*/
const [errors, setErrors] = useState({});
/**
* 表单数据变更处理函数
*/
const setFieldData = (name, value) => {
// 进行参数校验
if (validators && validators[name]) {
const error = validators[name](value);
if (error) {
setErrors((errors) => ({...errors, [name]: error}));
return;
}
setErrors((errors) => {
const newErrors = {...errors};
delete newErrors[name];
return newErrors;
})
}
// 更新表单数据
setFormData({
...formData,
[name]: value
});
}
/**
* 表单提交处理函数
*/
const handleSubmit = (e) => {
e.preventDefault();
if (errors && Object.keys(errors).length > 0) {
console.log('表单校验未通过');
return;
}
if (submitFunc) {
console.log('开始执行提交函数');
submitFunc(formData);
}
}
return (
<>
<div>
<form onSubmit={handleSubmit}>
{
children.map((child, index) => {
return (
<FormItem
key={index}
name={child.props.name}
label={child.props.label}
error={errors[child.props.name]}
type={child.props.type}
setFieldData={setFieldData}
>
{child}
</FormItem>
)
})
}
</form>
</div>
</>
)
}
/**
* 表单项组件
*/
const FormItem = ({name, type, error, label, fieldData, setFieldData}) => {
if (type === 'submit') {
return (
<div>
<input type="submit" value={label}/>
</div>
)
} else if (type === 'text') {
return (
<div>
<label htmlFor={name}>{label}</label>
<input type="text" name={name} value={fieldData} onChange={e => setFieldData(name, e.target.value)}/>
{error && <span>{error}</span>}
</div>
)
} else if (type === 'password') {
return (
<div>
<label htmlFor={name}>{label}</label>
<input type="password" name={name} value={fieldData}
onChange={e => setFieldData(name, e.target.value)}/>
{error && <span>{error}</span>}
</div>)
}
return null;
}
export {Form, FormItem};
使用样例
效果图见下图,使用样例代码见下方代码。
function App() {
return (<div>
<Form submitFunc={(data) => console.log(data)} initial_data={{username: 'vicyor', password: '123456'}}
validators={{
password: (val) => {
if (val.length < 6) {
return '密码长度不能小于6';
}
}
}}>
< FormItem name="username" label="用户名" type='text'/>
<FormItem name="password" label="密码" type='password'/>
<FormItem name="submit" label="提交" type='submit'/>
</Form>
</div>);
}