文章目录
- 前言
- Input组件
- 1. 功能分析
- 2. 代码+详细注释
- 3. 使用方式
- 4. 效果展示
- 总结
前言
今天我们来封装一个input输入框组件,并提供一些常用的功能,你可以选择不同的 尺寸、添加前缀、显示加载状态、触发回调函数、自定义样式 等等。这些功能在这个项目中已经足够了,无论你是一个经验丰富的开发者还是一个刚刚入门的新手,这篇文章都将提供有用的知识和实践经验,以帮助你在自己项目中封装输入框时更加高效
Input组件
1. 功能分析
(1)通过传入loading加载状态属性,当激活时会显示加载图标
(2)通过传入size属性, 可以有不同的大小:“default”、“small” 或 “large”
(3)提供onChange值发生变化回调函数;失去焦点onBlur回调函数;键盘回车onKeyUp回调函数;
(4)当输入框有值时,组件会显示一个清除按钮,用户可以通过点击按钮来清除输入框的值
(5)通过传入className属性, 可以自定义输入框的样式
(6)通过 elprops 属性将其他属性传递给底层的 input 元素
2. 代码+详细注释
// @/components/Input/index.tsx
import { FC, ReactNode, useCallback, useState, ChangeEventHandler, FocusEvent, ComponentPropsWithoutRef, KeyboardEventHandler } from "react";
import classNames from "classnames";
import { InputContainer } from "./styled";
import { ReactComponent as SearchIcon } from "./assets/search.svg";
import { ReactComponent as LoadingIcon } from "./assets/loading.svg";
import { ReactComponent as ClearIcon } from "./assets/clear.svg";
import SimpleButton from "@/components/SimpleButton";
// 组件的属性类型
type Props = Omit<ComponentPropsWithoutRef<"input">, "size"> & {
// 按下回车的回调
onEnter?: () => void;
// 是否显示加载状态
loading?: boolean;
// 输入框大小
size?: "default" | "small" | "large" | undefined;
// 输入框前缀
prefix?: boolean | ReactNode;
};
// 输入框组件
const Input: FC<Props> = ({ loading, size, onEnter, value: propsValue, onChange: propsOnChange, onKeyUp: propsOnKeyUp, onBlur: propsOnBlur, className, ...elprops }) => {
// 输入框的值,通过状态管理
const [value, setValue] = useState(propsValue);
// 输入事件
const handlerChange: ChangeEventHandler<HTMLInputElement> = useCallback(
(event) => {
// 如果正在加载,直接返回
if (loading) {
return;
}
// 更新状态和回调
setValue(event.target.value);
propsOnChange?.(event);
},
[propsOnChange, setValue, loading]
);
// 回车事件
const onKeyUp: KeyboardEventHandler<HTMLInputElement> = useCallback(
(event) => {
// 如果是回车键,触发回调
const isEnter = event.keyCode === 13;
if (isEnter) {
onEnter?.();
}
propsOnKeyUp?.(event);
},
[onEnter, propsOnKeyUp]
);
// 失去焦点事件
const onBlur = useCallback(
(event: FocusEvent<HTMLInputElement>) => {
propsOnBlur?.(event);
},
[propsOnBlur]
);
return (
<InputContainer className={classNames(className)} size={size}>
{/* 加载状态图标 */}
{loading ? <LoadingIcon className={classNames("input-loading-icon")} /> : <SearchIcon className={classNames("input-search-icon")} />}
{/* 输入框 */}
<input enterKeyHint="search" value={value} onChange={handlerChange} onKeyUp={onKeyUp} onBlur={onBlur} {...elprops} />
{/* 清除按钮 */}
{value && (
<SimpleButton className={classNames("input-clear-icon")} title="clear" onClick={() => setValue("")}>
<ClearIcon />
</SimpleButton>
)}
</InputContainer>
);
};
export default Input;
------------------------------------------------------------------------------
// @/components/Input/styled.tsx
import styled from "styled-components";
import variables from "@/styles/variables.module.scss";
type InputProps = {
size: "default" | "small" | "large" | undefined;
};
export const InputContainer = styled.div<InputProps>`
@keyframes rotate {
100% {
transform: rotate(360deg);
}
}
position: relative;
margin: 0 auto;
width: 100%;
height: ${({ size }) => {
if (size === "default") return "var(--cd-input-height)";
else if (size === "small") return "var(--cd-input-sm-height)";
else return "var(--cd-input-large-height)";
}};
padding-right: 8px;
display: flex;
align-items: center;
justify-content: center;
background: white;
border: 0 solid white;
border-radius: 4px;
input {
position: relative;
width: 100%;
height: 100%;
font-size: 14px;
padding: 0 8px;
background: #fff;
color: #333;
border: 0 solid #fff;
border-radius: 5px;
&:focus {
color: #333;
outline: none;
}
&::placeholder {
color: #888;
}
@media (max-width: ${variables.mobileBreakPoint}) {
font-size: 12px;
width: 100%;
padding-left: 6px;
padding-right: 16px;
}
}
.input-loading-icon,
.input-search-icon {
flex-shrink: 0;
width: 20px;
height: 20px;
margin-left: 8px;
}
.input-loading-icon {
animation: rotate 2s linear infinite;
}
.input-clear-icon {
display: flex;
align-items: center;
flex-shrink: 0;
}
`;
3. 使用方式
// 引入组件
import Input from '@/components/Input'
// 使用
const [loading, setLoading] = useState(false);
const [searchkeyword, setSearchkeyword] = useState("");
{/* 输入框值变化回调事件 */}
const onInputChange: ChangeEventHandler<HTMLInputElement> = (event) => {
console.log("onInputChange", event.target.value);
setSearchkeyword(event.target.value);
};
{/* 失焦回调事件 */}
const onInputBlur = () => {};
{/* 小尺寸,不带loading */}
<Input placeholder="请输入" size="small" />
{/* 标准尺寸,带loading */}
<Input placeholder="请输入" loading={loading} onChange={onInputChange} onBlur={onInputBlur} />
{/* 大尺寸,不带loading */}
<Input placeholder="请输入" size="large" />
{/* 带前缀 */}
<Input hasPrefix placeholder="请输入" loading={loading}} />
{/* 不带前缀 */}
<Input placeholder="请输入" loading={loading} onChange={onInputChange} onBlur={onInputBlur} />
4. 效果展示
(1)输入后,加载效果如下
注:如请求数据时添加加载状态,请求结束后取消加载状态
(2)点击清除按钮,清除数据效果
(3)三种尺寸显示如下
(4)带前缀 / 不带前缀效果
总结
下一篇讲【全局常用Search组件封装】。关注本栏目,将实时更新。