antd二次封装select

450 阅读1分钟

定义选中的是id还是name,...rest将其原有的select进行透传

import { Select } from "antd";
import PropTypes from "prop-types";
import { ComponentProps } from "react";

interface Option {
	id: string;
	name: string;
}

interface MySelectProps {
	options: Option[];
	valueType?: "id" | "name";
	value?: string[];
	defaultValue?: string[];
	onChange?: (value: string[]) => void;
}

type SelectProps = ComponentProps<typeof Select>;
type Props = MySelectProps & Omit<SelectProps, "value" | "onChange">;
//默认是根据id,可以修改为name
const MySelect: React.FC<Props> = ({ options, valueType = "id", value, onChange, ...rest }) => {
	const getValue = (option: Option) => {
		return valueType === "id" ? option.id : option.name;
	};

	const handleChange = (value: string[]) => {
		if (onChange) {
			onChange(value);
		}
	};

	return (
		<Select value={value} onChange={handleChange} {...rest}>
			{options.map(option => (
				<Select.Option key={option.id} value={getValue(option)}>
					{option.name}
				</Select.Option>
			))}
		</Select>
	);
};

MySelect.propTypes = {
	options: PropTypes.arrayOf(
		PropTypes.shape({
			id: PropTypes.string.isRequired,
			name: PropTypes.string.isRequired
		}).isRequired
	).isRequired,
	valueType: PropTypes.oneOf(["id", "name"]),
	value: PropTypes.arrayOf(PropTypes.string.isRequired),
	onChange: PropTypes.func
};

export default MySelect;

使用方法

import MySelect from "@/components/Select/index";

// 多选或者disabled等属性进行透传,类似跟vue的v-bind='$attrs'
<MySelect options={options} valueType="name" mode="multiple" onChange={handleSelectChange} />

const handleSelectChange = (values: string[]) => {
	console.log("Selected values:", values);
};

注意点: 类型校验

// 传入的数组可能是空数组,字符串数组,数组对象
	options: PropTypes.oneOfType([
		PropTypes.arrayOf(
			PropTypes.shape({
				id: PropTypes.string.isRequired,
				name: PropTypes.string.isRequired
			}).isRequired
		),
		PropTypes.arrayOf(PropTypes.string.isRequired),
		PropTypes.array
	]).isRequired,

根据数据不同去渲染

<Select className="mb-10 mr-10" value={value} onChange={handleChange} {...rest} style={{ width: "200px" }}>
			{(options as (Option | string)[]).map(option => {
                        // 空数组或字符串数组
				if (!option || typeof option === "string") {
					return (
						<Select.Option key={option} value={option}>
							{option}
						</Select.Option>
					);
				} else {
					return (
						<Select.Option key={`fragment-${option.id}`} value={getValue(option)}>
							{option.name}
						</Select.Option>
					);
				}
			})}
		</Select>

显示输入框,筛选选项以及输入选项之外的值

(原理就是失去焦点通过onChange传过来的父方法进行setData,要排除一下点击选中选项,这个时候的e.target.value是空,直接返回即可)

<Select className="mb-10 mr-10"
    showSearch
    value={value}
    onChange={handleChange}
    onBlur={handleBlur}
    {...rest}
    style={{ width: "200px" }}
    >
    
  const handleBlur = (e: any) => {
    if (!e.target.value) return;
    // 失去焦点调用父组件的方法进行赋值
    handleChange(e.target.value);
};

好了,二次封装的select就满足需求了