Anotaciones TS de uso común en React

Tomando componentes funcionales como ejemplo, organice los métodos de escritura TS utilizados en el proyecto. Continúe reponiendo. . .

1, Reaccionar.FC

FC proporcionó un tipo implícito (ReactElement | null) para niños antes de @types/react18, incluso si su anotación Props no define niños, aún puede deconstruirla en el parámetro. Las declaraciones se pueden mostrar en caso de problemas de incompatibilidad de tipochildren?: React.ReactNode

Para usar props en el cuerpo de la función debe definirlo explícitamente, después de usar esto, no hay necesidad de declararlo con PropsType.

interface BaseButtonProps{
  disabled?: boolean;
  size?: ButtonSize;
}
export const Button: React.FC<ButtonProps> = (props) => {
  const {
    disabled,
    size,
    children,
  } = props
  ...
}
复制代码

No es necesario marcar las anotaciones de accesorios como de solo lectura. Porque ReadOnly se agrega automáticamente cuando se agrega a un componente genérico

2, estado de uso

Deducir o pasar automáticamente genéricos

 //自动推导出user为string类型
const [user]=React.useState('a')

//传入泛型
interface DataSourceObject{
  value: string;
}
type DataSourceType<T = {}> =  T & DataSourceObject
const [suggestions, setSuggestions] = useState<DataSourceType[]>([])
复制代码

Cuando el estado inicial es nulo, el tipo declarado debe mostrarse


type User = {name:string} 
//例子1
const [user, setUser] = React.useState<User | null>(null)
//例子2
const [user,setUser] = React.useState<User>({} as User)
复制代码

3, useRef

// 如果可以的话尽量使用的类型具体点 
// 比如使用HTMLDivElement 就比HTMLElement好很多,比Element好更多 

function Foo(){ 
  const divRef = React.useRef<HTMLDivElement|null>(null); 
  return <div ref={divRef}>etc<div/> 
}

// 存储button dom
const buttonRef = React.useRef<HTMLButtonElement | null>(null);

// 存储a dom
const linkRef = React.useRef<HTMLLinkElement | null>(null);

复制代码

4, use ImperativeHandle, forwardRef

export interface MyInputHandles {
    focus:() => void;
}

const MyInput: RefForwardingComponent<MyInputHandles, MyInputProps> = (props, ref) => {
    const inputRef = useRef<HTMLInputElement>(null);
    useImperativeHandle(ref, () =>({
        focus: () => {
            if(inputRef.current) {
                inputRef.current.focus();
            }
        }
    }))
    return <Input {...props} ref={inputRef}>    
}

export default forwardRef(MyInput)
复制代码

5, usar contexto

Estos tres se utilizan a menudo juntos para la transferencia de datos entre componentes de nivel cruzado.La versión ts es la siguiente:

useContext toma un objeto de contexto (el valor de retorno de React.createContext) y devuelve el valor actual del contexto. El valor de contexto actual está determinado por la propiedad de valor en el componente superior que está más cerca del componente actual. La sintaxis es la siguiente:const value = useContext(MyContext);

import React, {useContext} from "react";

type SelectCallback= (selectIndex: string) => void

interface IMenuContext{
  index: string;
  onSelect?: SelectCallback;
  defaultOpenSubMenus?: string[];
}

export const MenuContext = createContext<IMenuContext>({ index: '0' })

const Demo3Child: React.FC<IProps> = () => {
     ...
    const context = useContext(MenuContext);
    return (
        <div>
            {context}
        </div>
    );
}

const Demo3: React.FC<IDemoProps> = () => {
    const passedContext: IMenuContext = {
        index: currentActive ? currentActive : '0',
        onSelect: handleClick,
        defaultOpenSubMenus,
     }
    return (
        <MyContext.Provider value={passedContext}>
            <Demo3Child />
        </MyContext.Provider>
    );
};
复制代码

6. useCallback y useMemo

useMemoAl igual que los useCallbackparámetros recibidos, todos se ejecutan después de que sus dependencias hayan cambiado y todos devuelven el valor almacenado en caché, la diferencia es que lo que se useMemodevuelve es el resultado de la ejecución de la función y useCallbacklo que se devuelve es la función. Los genéricos se pueden pasar o inferir automáticamente. El tipo genérico de useMemo especifica el tipo de valor de retorno y el tipo genérico de useCallback especifica el tipo de parámetro

useCallback

function useCallback<T extends (...args: any[]) => any>(callback: T, deps: DependencyList): T;

// 自动推断 (value: number) => number
const multiply = React.useCallback((value: number) => value * multiplier, [
  multiplier,
])

//泛型
const handleChange = React.useCallback<
  React.ChangeEventHandler<HTMLInputElement>
>(evt => {
  console.log(evt.target.value)
}, [])
复制代码

useMemo

function useMemo(factory: () => T, deps: DependencyList | undefined): T;

interface IClass{
  classId: number,
  className: string,
  type: number,
  isGradeLeader:number
}
...
const [classInfo, setClassInfo] = useState<IClass[]>([])
const adminiClasses: IClass[] = useMemo(() => classInfo.filter(item => item.type === 1), [classInfo])
const teachingClasses: IClass[] = useMemo(() => classInfo.filter(item => item.type === 2), [classInfo])
//自动推断useMemo的泛型 <IClass[]>
复制代码

7、事件处理函数

7.1、不带event参数

const Test: React.FC = () => {
    const handleClick = () => {
        // 做一系列处理
    };
    return (
        <div>
            <button onClick={handleClick}>按钮</button>
        </div>
    );
};
复制代码

7.2、带event参数

不做处理,直接写铁定报错

const Test: React.FC<{}> = () => {
    const handleClick: React.MouseEvent<HTMLButtonElement> = event => {
        // 做一系列处理
        event.preventDefault();
    };
    return (
        <div>
            <button onClick={handleClick}>按钮</button>
        </div>
    );
};
// onClick是MouseEventHandler类型
onClick?: MouseEventHandler<T> | undefined;

// 那MouseEventHandler<T>是个类型别名,泛型是Element类型
type MouseEventHandler<T = Element> = EventHandler<MouseEvent<T>;

//泛型Element是一个接口,通过继承该接口实现了很多其它接口
interface Element { }
interface HTMLElement extends Element { }
interface HTMLButtonElement extends HTMLElement { }
interface HTMLInputElement extends HTMLElement { }
//建议传入更为具体的element接口,
复制代码

对于其他事件也一样

7.3、事件类型列表

  • AnimationEvent : css动画事件
  • ChangeEvent:<input><select><textarea>元素的change事件
  • ClipboardEvent: 复制,粘贴,剪切事件
  • CompositionEvent:由于用户间接输入文本而发生的事件(例如,根据浏览器和PC的设置,如果你想在美国键盘上输入日文,可能会出现一个带有额外字符的弹出窗口)
  • DragEvent:在设备上拖放和交互的事件
  • FocusEvent::元素获得焦点的事件
  • FormEvent:当表单元素得失焦点/value改变/表单提交的事件
  • InvalidEvent: 当输入的有效性限制失败时触发(例如<input type="number" max="10">,插入数字20)
  • KeyboardEvent: 键盘键入事件
  • MouseEvent: 鼠标移动事件
  • PointerEvent: 鼠标、笔/触控笔、触摸屏)的交互而发生的事件
  • TouchEvent: 用户与触摸设备交互而发生的事件
  • TransitionEvent: CSS Transition,浏览器支持度不高
  • UIEvent:鼠标、触摸和指针事件的基础事件。
  • WheelEvent:在鼠标滚轮或类似的输入设备上滚动
  • SyntheticEvent:所有上述事件的基础事件。是否应该在不确定事件类型时使用

因为InputEvent在各个浏览器支持度不一样,所以可以使用KeyboardEvent代替

8、普通函数

普通函数是通用的

  • 一个具体类型的输入输出函数;
// 参数输入为number类型,通过类型判断直接知道输出也为number
function testFun1 (count: number) {
    return count * 2;
}
复制代码
  • 一个不确定类型的输入、输出函数,但是输入、输出函数类型一致;
// 用泛型
function testFun2<T> (arg: T): T {
    return arg;
}
复制代码
  • async函数,返回的为Promise对象,可以使用then方法添加回调函数,Promise是一个泛型函数,T泛型变量用于确定then方法时接收的第一个回调函数的参数类型。
// 用接口
interface IResponse<T> {
    result: T,
    status: string
};

// 除了用接口外,还可以用对象
// type IResponse<T> = {
//      result: T,
//    status: string
// };

async function testFun3(): Promise<IResponse<number>> {
    return {
        status: 'success',
        result: 10
    }
}
testFun3.then(res=>{console.log(res)})
复制代码

9、 TS内置类型

  • Partial:使对象中的所有属性都是可选的
interface IUser { name: string age: number department: string }
经过 Partial 类型转化后得到

type optional = Partial<IUser>

// optional的结果如下
type optional = {
    name?: string | undefined;
    age?: number | undefined;
    department?: string | undefined;
}
复制代码
  • Omit:生成一个新类型,该类型拥有 T 中除了 K 属性以外的所有属性
type Foo = { name: string age: number } 
type Bar = Omit<Foo, 'age'> 
// 相当于 type Bar = { name: string }
复制代码
  • ConstructorParameters :类构造函数参数类型的元组
  • Exclude:将一个类型从另一个类型排除
  • Extract:选择可分配给另一类型的子类型
  • InstanceType:从一个新类构造函数中获得的实例类型
  • NonNullable:从类型中排除空和未定义
  • Parameters:函数的形参类型的元组
  • Readonly:将对象中的所有属性设置为只读
  • ReadonlyArray:创建给定类型的不可变数组
  • Pick:对象类型的一种子类型,包含其键的子集
  • Record:从键类型到值类型的映射
  • Required:使对象中的所有属性都是必需的
  • ReturnType:函数的返回类型

这里有一个博客写的很详细,可以参考:TS 里几个常用的内置工具类型

10、自定义hook

自定义 Hook 的返回值如果是数组类型,TS 会自动推导为 Union 类型,解决:const断言

function useLoading() {
    const [isLoading, setState] = React.useState(false)
    const load = (aPromise: Promise<any>) => {
        setState(true)
        return aPromise.then(() => setState(false))
    }
    return [isLoading, load] as const //这个hook把isLoading和一个函数返回出去
    // 实际需要: [boolean, typeof load] 类型
    // 而不是自动推导的:(boolean | typeof load)[]
}

//可以定义一个方法可以统一处理 tuple(元组) 返回值
function tuplify<T extends any[]>(...elements: T) {
  return elements
}

把上面例子 return [isLoading, load] as const   改为=>   return tuplify(isLoading, load)
复制代码

11、React.ComponentProps

获得组件的props类型可以使用React.ComponentProps<typeof Button>

例如:子组件必须接受一个name参数,父组件要把它的props传入子组件,那么父组件要拿到子组件这个必要的类型来定义自己的Iprops。使用React.ComponentProps拿到子组件必要类型,再用交叉类型&,定义自己的Iprops。

通过查找类型减少 type 的非必要导出,如果需要提供复杂的 type,应当提取到作为公共 API 导出的文件中。

// counter.tsx
import * as React from 'react'
export type Props = {
  name: string
}
const Counter: React.FC<Props> = props => {
  return <></>
}
export default Counter

//app.tsx
import Counter from './d-tips1'
type PropsNew = React.ComponentProps<typeof Counter> & {
  age: number//这是app组件要的
}
const App: React.FC<PropsNew> = props => {
  return <Counter {...props} />
}
复制代码

12、特定类型的React.children

React.Children 提供了用于处理 this.props.children 不透明数据结构的实用方法。当用在在父组件中规定其children只能是特性的组件时,就需要做一些特殊处理。例如:规定Menu组件里面只能是MenuItem或SubMenu,而不能是其他元素,可以这样写

//Menu.tsx
import { MenuItemProps } from './menuItem'
const renderChildren = () => {
    return React.Children.map(children, (child, index) => {
     // 限制Menu下面只能是MenuItem或SubMenu,不能是其它元素
      const childElement = child as React.FunctionComponentElement<MenuItemProps>
      const { displayName } = childElement.type
      if (displayName === 'MenuItem' || displayName === 'SubMenu') {
        return React.cloneElement(childElement, { index: index.toString() }) 
        //将index混入到实例中
      } else {
        console.error('Warning:Menu has a child which is not a MenuItem or SubMenu component')
      }
    })
}
...
//menuItem.tsx
export interface MenuItemProps{
  disabled?: boolean;
  index?: string;
  className?: string;
  style?: React.CSSProperties;
}
const MenuItem:React.FC<MenuItemProps>=(props)=>{
    return (
     <li>{children}</li>
    )
}
MenuItem.displayName='MenuItem'
export defult MenuItem
复制代码

Supongo que te gusta

Origin juejin.im/post/7078557323437965343
Recomendado
Clasificación