文章目录
- 1 react表单组件
- 1.1 受控组件 (Controlled Components)
- 示例代码:
- 1.2 非受控组件 (Uncontrolled Components)
- 示例代码:
- 2 AntD表单组件实战
- 2.1 开发搜索功能
- 2.2 开发注册页
- 2.3 开发登录页
- 2.4 表单组件校验
- 结语
1 react表单组件
input表单组件
import { ChangeEvent, useState } from "react";
function App() {
const [text, setText] = useState<string>("hello");
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setText(e.target.value);
}
return (
<>
<p>APP</p>
<div>
<input value={text} onChange={handleChange} />
</div>
);
}
export default App;
1.1 受控组件 (Controlled Components)
通过 React 的 state
和 onChange
事件动态管理表单输入的值,表单数据由 React 组件控制。
示例代码:
import React, { useState } from 'react';
function ControlledForm() {
const [formData, setFormData] = useState({
username: '',
email: '',
password: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
const handleSubmit = (e) => {
e.preventDefault();
console.log('提交数据:', formData);
// 发送到 API 或进一步处理
};
return (
<form onSubmit={handleSubmit}>
<input
type="text"
name="username"
value={formData.username}
onChange={handleChange}
placeholder="用户名"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="邮箱"
/>
<input
type="password"
name="password"
value={formData.password}
onChange={handleChange}
placeholder="密码"
/>
<button type="submit">提交</button>
</form>
);
}
1.2 非受控组件 (Uncontrolled Components)
使用 ref
直接访问 DOM 元素的值,适用于简单场景或需要手动操作 DOM 的情况。
示例代码:
import React, { useRef } from 'react';
function UncontrolledForm() {
const usernameRef = useRef();
const emailRef = useRef();
const passwordRef = useRef();
const handleSubmit = (e) => {
e.preventDefault();
const data = {
username: usernameRef.current.value,
email: emailRef.current.value,
password: passwordRef.current.value
};
console.log('提交数据:', data);
};
return (
<form onSubmit={handleSubmit}>
<input type="text" ref={usernameRef} placeholder="用户名" />
<input type="email" ref={emailRef} placeholder="邮箱" />
<input type="password" ref={passwordRef} placeholder="密码" />
<button type="submit">提交</button>
</form>
);
}
2 AntD表单组件实战
2.1 开发搜索功能
基础ListSearch.tsx代码如下:
import { FC, useState } from "react";
import type { ChangeEvent } from "react";
import { Input } from "antd";
const { Search } = Input;
const ListSearch: FC = () => {
const [value, setValue] = useState("");
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setValue(e.target.value);
}
function handleSearch(value: string) {
console.log(value);
}
return (
<Search
size="large"
allowClear
placeholder="输入关键字"
value={value}
onChange={handleChange}
onSearch={handleSearch}
style={{ width: "60%" }}
/>
);
};
export default ListSearch;
效果如下图所示:
搜索之后,如果刷新页面,我们希望展示内容和刷新之前一样,需要我们把搜索“参数值”放入URL,改造之后的代码如下:
import { FC, useEffect, useState } from "react";
import type { ChangeEvent } from "react";
import { useNavigate, useLocation, useSearchParams } from "react-router-dom";
import { Input } from "antd";
import { LIST_SEARCH_PARAM_KEY } from "../constant";
const { Search } = Input;
const ListSearch: FC = () => {
const nav = useNavigate();
const { pathname } = useLocation();
const [value, setValue] = useState("");
function handleChange(e: ChangeEvent<HTMLInputElement>) {
setValue(e.target.value);
}
// 获取url参数,并设置input value
const [searchParams] = useSearchParams();
useEffect(() => {
const curVal = searchParams.get(LIST_SEARCH_PARAM_KEY) || "";
setValue(curVal);
}, [searchParams]);
function handleSearch(value: string) {
nav({
pathname,
search: `${LIST_SEARCH_PARAM_KEY}=${value}`,
});
}
return (
<Search
size="large"
allowClear
placeholder="输入关键字"
value={value}
onChange={handleChange}
onSearch={handleSearch}
style={{ width: "60%" }}
/>
);
};
export default ListSearch;
效果如下图所示:
2.2 开发注册页
Register.tsx基础代码如下:
import { FC } from "react";
import { Link } from "react-router-dom";
import { Typography, Space, Form, Input, Button } from "antd";
import { UserAddOutlined } from "@ant-design/icons";
import { LOGIN_PATHNAME } from "../router";
import styles from "./Register.module.scss";
const { Title } = Typography;
const Register: FC = () => {
function onFinish(values: any) {
// todo 注册api
console.log(values);
}
return (
<div className={styles.container}>
<div>
<Space>
<Title level={2}>
<UserAddOutlined />
</Title>
<Title level={2}>注册新用户</Title>
</Space>
</div>
<div>
<Form
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
onFinish={onFinish}
>
<Form.Item label="用户名" name="username">
<Input />
</Form.Item>
<Form.Item label="密码" name="password">
<Input.Password />
</Form.Item>
<Form.Item label="确认密码" name="confirm">
<Input.Password />
</Form.Item>
<Form.Item label="昵称" name="nickname">
<Input />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
<Space>
<Button type="primary" htmlType="submit">
注册
</Button>
<Link to={LOGIN_PATHNAME}>已有账户,登录</Link>
</Space>
</Form.Item>
</Form>
</div>
</div>
);
};
export default Register;
带完善功能:
- 样式优化
- 表单校验,比如用户名特殊字符校验,密码安全校验,确认密码一致校验等
2.3 开发登录页
页面和注册相似,表单组件变少,Login.tsx代码如下:
import { FC, useEffect } from "react";
import { Link } from "react-router-dom";
import { Typography, Space, Form, Input, Button, Checkbox } from "antd";
import { UserAddOutlined } from "@ant-design/icons";
import { REGISTER_PATHNAME } from "../router";
import styles from "./Register.module.scss";
import Password from "antd/es/input/Password";
const { Title } = Typography;
const USERNAME_KEY = "usename";
const PASSWORD_KEY = "password";
/**
* 浏览器本地存储用户信息
* @param username 用户名
* @param password 密码
*/
function rememberUser(username: string, password: string) {
localStorage.setItem(USERNAME_KEY, username);
localStorage.setItem(PASSWORD_KEY, password);
}
/**
* 浏览器本地删除用户信息
* @param username 用户名
* @param password 密码
*/
function deleteUserFromStorage(username: string, password: string) {
localStorage.removeItem(USERNAME_KEY);
localStorage.removeItem(PASSWORD_KEY);
}
/**
* 浏览器本地获取用户信息
*/
function getUserInfoFromStorage() {
return {
username: localStorage.getItem(USERNAME_KEY),
password: localStorage.getItem(PASSWORD_KEY),
};
}
const Login: FC = () => {
// 表单组件初始化
const [form] = Form.useForm();
useEffect(() => {
const { username, password } = getUserInfoFromStorage();
form.setFieldsValue({ username, password });
}, []);
function onFinish(values: any) {
// todo 注册api
console.log(values);
const { username, password, remember } = values || {};
if (remember) {
rememberUser(username, password);
} else {
deleteUserFromStorage(username, password);
}
}
return (
<div className={styles.container}>
<div>
<Space>
<Title level={2}>
<UserAddOutlined />
</Title>
<Title level={2}>用户登录</Title>
</Space>
</div>
<div>
<Form
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
onFinish={onFinish}
initialValues={{ remember: true }}
form={form}
>
<Form.Item label="用户名" name="username">
<Input />
</Form.Item>
<Form.Item label="密码" name="password">
<Input.Password />
</Form.Item>
<Form.Item
wrapperCol={{ offset: 6, span: 16 }}
name="remember"
valuePropName="checked"
>
<Checkbox>记住我</Checkbox>
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
<Space>
<Button type="primary" htmlType="submit">
登录
</Button>
<Link to={REGISTER_PATHNAME}>注册新用户</Link>
</Space>
</Form.Item>
</Form>
</div>
</div>
);
};
export default Login;
解析:
- Form.useForm():antd提供的第三方hooks
- LocalStorage:浏览器本地存储对象
- 记住我:勾选“记住我”,存储用户信息;不勾选,清理存储的用户信息;页面初始化时,回显用户信息
2.4 表单组件校验
antd表单校验详细参考下面链接1,这里以注册页表单校验为例,演示常见校验规则配置,Register.tsx代码如下:
...
const Register: FC = () => {
...
return (
<div className={styles.container}>
...
<div>
<Form
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
onFinish={onFinish}
>
<Form.Item
label="用户名"
name="username"
rules={[
{ required: true, message: "请输入用户名" },
{
type: "string",
min: 5,
max: 20,
message: "字符长度再5-20之间",
},
{
pattern: /^\w$/,
message: "只能是字母数字下划线",
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[
{ required: true, message: "请输入用户名" },
{
min: 8,
message: "密码长度最少8位",
},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
label="确认密码"
name="confirm"
dependencies={["password"]}
rules={[
{
required: true,
message: "请输入确认密码",
},
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue("password") === value) {
return Promise.resolve();
} else {
return Promise.reject(new Error("两次密码不一致"));
}
},
}),
]}
>
<Input.Password />
</Form.Item>
...
</Form>
</div>
</div>
);
};
export default Register;
效果如下图所示:
结语
❓QQ:806797785
⭐️仓库地址:https://gitee.com/gaogzhen
⭐️仓库地址:https://github.com/gaogzhen
[1]ant design 官网[CP/OL].