如果让你设计一个简单的 Form 表单组件,不考虑复杂的情况。只需要考虑收集值以及设置默认值。你会如何设计呢?用什么方式收集数据?用什么方式设置默认值?
👁 效果展示
🤔 实现思路
- 对于收集数据来说,主要是使用 FormData 进行处理!
- 对于设置默认值,则是使用 React.Children.map 进行处理!
✍️ 设计组件
🎮 Form 组件
import { Children, cloneElement } from "react";
import { Input } from "./Input";
import { Button } from "./Button";
export const Form = ({ children, onSubmit, initialValue, ...props }) => {
const handleSubmit = (e) => {
e.preventDefault();
const formData = new FormData(e.currentTarget);
const fieldValues = Object.fromEntries(formData.entries());
onSubmit(fieldValues);
};
return (
<form onSubmit={handleSubmit} {...props}>
{Children.map(children, (child) => {
if (child.type.name !== "Button") {
return cloneElement(child, {
...child.props,
initialValue: initialValue?.[child.props?.name],
});
}
return child;
})}
</form>
);
};
Form.Input = Input;
Form.Button = Button;
📚 Input 组件
import styled from "styled-components";
import { useState } from "react";
const Input_ = styled.input`
box-sizing: border-box;
margin: 0;
font-variant: tabular-nums;
list-style: none;
font-feature-settings: "tnum";
position: relative;
display: inline-block;
width: 100%;
min-width: 0;
padding: 4px 11px;
color: #000000d9;
font-size: 14px;
line-height: 1.5715;
background-color: #fff;
background-image: none;
border: 1px solid #d9d9d9;
border-radius: 2px;
transition: all 0.3s;
outline: none;
`;
const Label = styled.label`
position: relative;
display: inline-flex;
align-items: center;
max-width: 100%;
height: 32px;
color: #000000d9;
font-size: 14px;
overflow: hidden;
white-space: nowrap;
text-align: right;
vertical-align: middle;
&::after {
content: ":";
position: relative;
top: -0.5px;
margin: 0 8px 0 2px;
}
`;
const ItemContainer = styled.div`
display: flex;
flex-flow: row wrap;
box-sizing: border-box;
margin: 0 0 24px;
padding: 0;
color: #000000d9;
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5715;
list-style: none;
font-feature-settings: "tnum";
vertical-align: top;
.item-width {
display: block;
flex: 0 0 33.33333333%;
max-width: 33.33333333%;
text-align: right;
}
`;
export const Input = ({ label, name, initialValue, onChange, ...props }) => {
const [value, setValue] = useState(initialValue ?? "");
const handleChange = (e) => {
setValue(e.target.value);
onChange?.(e);
};
return (
<ItemContainer>
<div className="item-width">
<Label htmlFor={`${label}-label`}>{label}</Label>
</div>
<div className="item-width">
<Input_
id={`${label}-label`}
value={value}
onChange={handleChange}
name={name}
type="text"
{...props}
/>
</div>
</ItemContainer>
);
};
🔗 Button 组件
import styled from "styled-components";
const Button_ = styled.button`
line-height: 1.5715;
position: relative;
display: inline-block;
font-weight: 400;
white-space: nowrap;
text-align: center;
background-image: none;
border: 1px solid transparent;
box-shadow: 0 2px #00000004;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
user-select: none;
touch-action: manipulation;
height: 32px;
padding: 4px 15px;
font-size: 14px;
border-radius: 2px;
color: #000000d9;
border-color: #d9d9d9;
background: #fff;
`;
export const Button = ({ children }) => {
return <Button_ type="submit">{children}</Button_>;
};
✋🏻 Usage
import { Form } from "./Component/Form";
function App() {
const handleSubmit = (fieldValues) => {
console.log(
"🚀 ~ file: App.js ~ line 5 ~ handleSubmit ~ fieldValues",
fieldValues
);
};
return (
<div className="App">
<Form
onSubmit={handleSubmit}
autoComplete="off"
initialValue={{
userName: "Destiny__",
emal: "nicai@nc.com",
tel: "123456789",
}}
>
<Form.Input label="姓名" name="userName" />
<Form.Input label="邮件" name="emal" type="email" />
<Form.Input label="手机" name="tel" type="tel" />
<Form.Button>提交</Form.Button>
</Form>
</div>
);
}
export default App;
🤝 👏 👋 往期精彩
- 👏React 组件优化第二弹来袭!!
- 👏这些避免React组件重复渲染的手段你都知道吗?
- 👏项目引入ESBuild,编译直接快上天!!
- 👏在React中使用WebComponents组件的最佳实践
- 👏我是怎样将50+MB的app打包文件优化为4.2MB的?
- 👏styleds-components 的原理你能讲一下吗?
- 👏面试官:你能讲一下extends和寄生式组合继承原型之间的区别?
- 👏前端Leader让我给同事讲讲事件循环
- 👏React与Vue状态更新原理对比
- 👏当React遇到树形穿梭框咋办?
- 👏使用React时要避免的 10 大错误
- 👏webpack打包优化方向指南(理论篇)
- 👏vue遇到拖拽动态生成组件怎么办?
- 👏JS Coding 技巧,大多数人都不会!!
- 👏TypeScript 它不香吗?还不快来!