在React中封装一个组件
背景
最近在学习React,看了许多教学视频,今天学到了一个封装组件较完善的方法,特此记录下来。
我们知道select标签经常有显示的问题,例如id和name对应不上,原因在于value属性的值:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
   | <Select           value={param.personId}           onChange={(value) =>             setParam({               ...param,               personId: value,             })           }         >           <Select.Option value={""}>负责人</Select.Option>           {users.map((user) => (             <Select.Option key={user.id} value={String(user.id)}>               {user.name}             </Select.Option>           ))}         </Select>
   | 
 
Option中的value值在传给onChange回调函数中时,如果是number类型,但personId定义的又是string类型的话,就不会按预期显示,而是直接用传入的值。
于是现在封装一个组件id-select,解决上述问题。
封装组件
在components文件夹下新建一个id-select.tsx文件,首先写好组件基本内容。
1 2 3 4 5 6 7 8 9 10 11 12
   | import { Select } from "antd"; import React from "react";
 
 
  export const IdSelect = () => {     return (         <Select value={} onChange={}>             <Select.Option value={}>{}</Select.Option>         </Select>     ) }
  | 
 
我们要封装一个Select组件,使得其
- value可以传入多种类型的值
 
- onChange只会回调number | undefined类型
 
- 当 isNaN(Number(value))为true 代表选择默认类型
 
- 当选择默认类型,onChange回调undefined
 
定义下组件所需的props类型:
1 2 3 4 5 6
   | interface IdSelectProps {   value: string | number | null | undefined;   onChange: (value?: number) => void;   defaultOptionName?: string;   options?: { name: string; id: number }[]; }
  | 
 
定义一个辅助函数:
1 2 3
   | const toNumber = (value: unknown) => {   return isNaN(Number(value)) ? 0 : Number(value); };
  | 
 
完善组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
   | export const IdSelect = (props: IdSelectProps) => {   const { value, onChange, defaultOptionName, options } = props;   return (     <Select       value={toNumber(value)}       onChange={(value) => onChange(toNumber(value) || undefined)}     >       {defaultOptionName ? (         <Select.Option value={0}>{defaultOptionName}</Select.Option>       ) : null}       {options?.map((option) => (         <Select.Option value={option.id} key={option.id}>           {option.name}         </Select.Option>       ))}     </Select>   ); };
  | 
 
还有一个问题,如果我们需要用到Select本身的props呢,或者说我想改变Select本身的样式呢?
这就是一个props透传问题
React中有专门的处理方法,将接口改为如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
   | // 继承Select组件本身的props type SelectProps = React.ComponentProps<typeof Select>;
  // 去掉我们接口中定义的,防止重名 interface IdSelectProps   extends Omit<     SelectProps,     "value" | "onChange" | "defaultOptionName" | "options"   > {   value: string | number | null | undefined;   onChange: (value?: number) => void;   defaultOptionName?: string;   options?: { name: string; id: number }[]; }
 
   | 
 
然后传给里面的Select组件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
   | export const IdSelect = (props: IdSelectProps) => {     // 用扩展操作符 ...rest取得props中剩余的属性   const { value, onChange, defaultOptionName, options, ...rest } = props;   return (     <Select       value={toNumber(value)}       onChange={(value) => onChange(toNumber(value) || undefined)}         // 传给Select       {...rest}     >       {defaultOptionName ? (         <Select.Option value={0}>{defaultOptionName}</Select.Option>       ) : null}       {options?.map((option) => (         <Select.Option value={option.id} key={option.id}>           {option.name}         </Select.Option>       ))}     </Select>   ); };
  | 
 
完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
   | import { Select } from "antd"; import React from "react";
  type SelectProps = React.ComponentProps<typeof Select>;
  interface IdSelectProps   extends Omit<     SelectProps,     "value" | "onChange" | "defaultOptionName" | "options"   > {   value: string | number | null | undefined;   onChange: (value?: number) => void;   defaultOptionName?: string;   options?: { name: string; id: number }[]; }
  /**  *  * value 可以传入都多种类型的值  * onChange只会回调 number | undefined 类型  * 当 isNaN(Number(value))为true 代表选择默认类型  * 当选择默认类型,onChange回调undefined  */ export const IdSelect = (props: IdSelectProps) => {   const { value, onChange, defaultOptionName, options, ...rest } = props;   return (     <Select       value={toNumber(value)}       onChange={(value) => onChange(toNumber(value) || undefined)}       {...rest}     >       {defaultOptionName ? (         <Select.Option value={0}>{defaultOptionName}</Select.Option>       ) : null}       {options?.map((option) => (         <Select.Option value={option.id} key={option.id}>           {option.name}         </Select.Option>       ))}     </Select>   ); };
  const toNumber = (value: unknown) => {   return isNaN(Number(value)) ? 0 : Number(value); };
 
  | 
 
一个简单的组件就封装好了
example
1 2 3 4 5 6 7 8 9
   | import React from "react"; import { useUser } from "screens/project-list/user"; import { IdSelect } from "./id-select";
  export const UserSelect = (props: React.ComponentProps<typeof IdSelect>) => {   const { users } = useUser();   return <IdSelect options={users || []} {...props}></IdSelect>; };
 
   |