使用ant design 表单组件,开发登录,注册,搜索功能
React 表单组件 ,受控组件
案列
使用defaultVlue属性
bug : 改变了数据源,但是页面未重新渲染
{/* 表单组件 */}
<button onClick={()=>{
console.log(text);
}}>打印</button>
<button onClick={()=>[
setText("hello"),
]}>set</button>
使用value
const [text , setText] = useState("hello world");
const handleChange=(e)=> {
setText(e.target.value)
}
--------------------------------------------
<input value={text} onChange={handleChange}></input>
<button onClick={()=>{
console.log(text);
}}>打印</button>
<button onClick={()=>[
setText("hello"),
]}>set</button>
<button onClick={()=>[
setText("hello"),
]}>set</button>
总结
- 受控组件:
值同步到state,使用value属性 - 非受控组件
值不同步到state,使用default属性
常用表单组件
受控组件
import {FC} from "react";
import { ChangeEvent, useState } from "react";
const Demo : FC =()=>{
const [text , setText] = useState<string>("hello world");
const handleChange=(e : ChangeEvent<HTMLInputElement>)=> {
setText(e.target.value)
}
//---------------------------------------------------------------------
const [tarea , setTarea] = useState<string>("hello");
const handleChange2=(e : ChangeEvent<HTMLTextAreaElement>)=> {
setTarea(e.target.value);
}
function getHtml() {
return {
__html: tarea.replaceAll("\n", "<br>")
}
}
//-------------------------------------------------------------------------
const [gender , setGender] = useState<string>("male");
const handleGeander=(e : ChangeEvent<HTMLInputElement>)=> {
setGender(e.target.value)
}
//-------------------------------------------------------------------------
const[selectdIdList,setSelectdIdList]=useState<string[]>([]);
const handleSelectd=(e : ChangeEvent<HTMLInputElement>)=> {
const city = e.target.value;
if(selectdIdList.includes(city)){
//移除
setSelectdIdList(selectdIdList.filter(item=>item!==city))
}else{
//添加
setSelectdIdList(selectdIdList.concat(city))
// setSelectdIdList([...selectdIdList,city])
}
}
//-------------------------------------------------------------------------
const [lang , setLang] = useState<string>("1");
const handleSelect=(e : ChangeEvent<HTMLSelectElement>)=> {
setLang(e.target.value)
}
return (
<div>
<h3>input 组件</h3>
<input value={text} onChange={handleChange}></input>
<button onClick={()=>{
console.log(text);
}}>打印</button>
<button onClick={()=>[
setText("hello"),
]}>set</button>
<h3>textarea 组件</h3>
<textarea value={tarea} onChange={handleChange2}></textarea>
{/* {tarea}//没有换行 */}
{/* {tarea.replaceAll("\n", "<br>")}//有xss注入的风险 */}
<div dangerouslySetInnerHTML={getHtml()} />
{/* 单选框 */}
<h3>单选框 组件</h3>
{/* 添加lable 实现点击文字也能选中效果 */}
<label htmlFor="male">male</label>
<input type="radio" id="male" name="sex" value="male" checked={gender === "male"} onChange={handleGeander} />
<label htmlFor="female">female</label>
<input type="radio" id="female" name="sex" value="female" checked={gender === "female"} onChange={handleGeander} />
{/* 复选框 */}
<h3>复选框 组件</h3>
<label htmlFor="1">shanghai</label>
<input
type="checkbox"
id="1"
value="shanghai"
checked={selectdIdList.includes("shanghai")}
onChange={handleSelectd}
/>
<label htmlFor="2">beijing</label>
<input
type="checkbox"
id="2"
value="beijing"
checked={selectdIdList.includes("beijing")}
onChange={handleSelectd}
/>
<label htmlFor="3">chengdu</label>
<input
type="checkbox"
id="3"
value="chengdu"
checked={selectdIdList.includes("chengdu")}
onChange={handleSelectd}
/>
{
JSON.stringify(selectdIdList)
}
{/* 下拉框 */}
<h3>下拉框 组件</h3>
<select value={lang} onChange={handleSelect}>
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
</select>
</div>
)
}
export default Demo;
form 组件
import {FC,useState,useRef, useEffect} from 'react'
const Demo:FC =()=>{
const handleSubmit = (e)=>{
e.preventDefault();//阻止默认行为
// js 提交数据
console.log(e.target.k1.value);
console.log(e.target.k2.value);
}
return(
<>
<h3>Form 组件</h3>
{/* 点击提交会触发请求,请求的地址是:https://www.baidu.com?k1=k1&k2=k2 \
提交的参数{
k1:v1;
k2:v2;
}
*/}
<form action="https://www.baidu.com" method="get">
<input name='k1' value='k1'/>
<br />
<br />
<input name='k2' value='k2' />
<br />
<br />
<input type="submit" value="提交" />
</form>
{/* 添加onsubmit,阻止默认行为 */}
<form action="https://www.baidu.com" method="get" onSubmit={handleSubmit}>
<input name='k1' value='k1'/>
<br />
<br />
<input name='k2' value='k2' />
<br />
<br />
<input type="submit" value="提交" />
</form>
</>
)
}
export default Demo;
ant design 表单组件
封装LisSearch组件
import {FC , useState ,useEffect} from 'react'
import { useNavigate , useLocation ,useSearchParams} from 'react-router-dom';
import { Input } from "antd";
//引入常量
import {LIST_SEARCH_PARAM_KEY} from '../constant/index.js';
const {Search} = Input;
const ListSearch: FC = () => {
//路由跳转
const nav = useNavigate();
//获取url
const {pathname} = useLocation();
//获取url参数,设置到搜索框中
const [searchParams] = useSearchParams();
useEffect(()=>{
const searchVal = searchParams.get(LIST_SEARCH_PARAM_KEY) || "";
//设置搜索框的值
setVal(searchVal)
},[searchParams])
const handleSearch =(val : string )=>{
//搜索时改变url,防止刷新丢失信息
//跳转页面,增加url参数
nav({
pathname: pathname,
search: `${LIST_SEARCH_PARAM_KEY}=${val}`
})
}
//受控组件
const [val, setVal] = useState<string>('');
const handleChange =(e : React.ChangeEvent<HTMLInputElement>)=>{
setVal(e.target.value)
}
return(
<div>
<Search value={val} onChange={handleChange} placeholder="请输入关键字" style={{ width: 200 }} onSearch={handleSearch} />
</div>
)
}
export default ListSearch;
表单组件的校验
ant design rules
校验之前
const Register : FC = ()=>{
const onfinish = (values) => {
console.log('Received values of form: ', values);
}
return (
<>
<div className={styles.container}>
<div>
<Space>
<Title level={2}>
<UserAddOutlined />
</Title>
<Title level={2}>注册新用户</Title>
</Space>
</div>
<div>
<Form
name="basic"
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
initialValues={{ remember: true }}
autoComplete="off"
onFinish={onfinish}
>
<Form.Item
label="用户名"
name="username"
>
<Input />
</Form.Item>
<Form.Item
label="密码"
name="password"
>
<Input.Password />
</Form.Item>
<Form.Item
label="确认秘密"
name="checkedPassword"
>
<Input.Password />
</Form.Item>
<Form.Item
label="昵称"
name="nickname"
>
<Input />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
<Space>
<Button type="primary" size='small' htmlType="submit">
注册
</Button>
<Link to={LOGIN_PATH}>已有账户, 登录</Link>
</Space>
</Form.Item>
</Form>
</div>
</div>
</>
)
}
校验之后
const Register : FC = ()=>{
const onfinish = (values) => {
console.log('Received values of form: ', values);
}
return (
<>
<div className={styles.container}>
<div>
<Space>
<Title level={2}>
<UserAddOutlined />
</Title>
<Title level={2}>注册新用户</Title>
</Space>
</div>
<div>
<Form
name="basic"
labelCol={{ span: 6 }}
wrapperCol={{ span: 16 }}
initialValues={{ remember: true }}
autoComplete="off"
onFinish={onfinish}
>
<Form.Item
label="用户名"
name="username"
rules={[
{required:true , message:'请输入用户名'},
{type: 'string', min : 5, max: 20 , message:'用户名长度为5-20'},
{pattern : /^[a-zA-Z0-9_]+$/ , message:'用户名只能包含字母、数字、下划线'}
]}
>
<Input />
</Form.Item>
<Form.Item
label="密码"
name="password"
rules={[
{required:true , message:'请输入密码'},
]}
>
<Input.Password />
</Form.Item>
<Form.Item
label="确认秘密"
name="confirm"
dependencies={["password"]}// 依赖项,password 变化时触发validateField
rules={[
{required:true , message:'请确认密码'},
({getFieldValue}) => ({
validator(_,val){
if(!val || getFieldValue("password") === val){
return Promise.resolve()
}else{
return Promise.reject(new Error('两次输入的密码不一致'))
}
}
})
]}
>
<Input.Password />
</Form.Item>
<Form.Item
label="昵称"
name="nickname"
>
<Input />
</Form.Item>
<Form.Item wrapperCol={{ offset: 6, span: 16 }}>
<Space>
<Button type="primary" size='small' htmlType="submit">
注册
</Button>
<Link to={LOGIN_PATH}>已有账户, 登录</Link>
</Space>
</Form.Item>
</Form>
</div>
</div>
</>
)
}
第三方表单校验工具
react -hook-form
React Hook Form - performant, flexible and extensible form library (react-hook-form.com)
formik
Formik: Build forms in React, without the tears