《React+TS》

203 阅读4分钟

在react项目里,遇到的ts语法:

1.

type Props={
    value: string[],
    onChange:(tags:string[])=>void
}
const TagSection : React.FC <Props> =(props)=>{
    const [tags,setTags]=useState<string[]>(['衣服','餐饮','住房','交通'])
}

定义一个函数组件TagSection,要指明它的类型:React.FC(Function Component)。

接受一个props外部属性的参数,如果这个props里不只有children,还有父组件给它传的一些变量,函数之类的,那就要声明这个props的类型。props也可以接受函数。声明函数的类型时,先写函数名,:,箭头函数的形式。括号里写参数的类型,没有参数就空着。没有返回值就写void。

参数props的类型用尖括号。<Props>

使用useState初始化一个数据,如果类型复杂,比如是一个字符串数组,也要指明类型。类型写在<>

2. 可以声明联合类型,缩小类型范围。

type Category='-' | '+'
function Money() {
    const [selectedRecord,setSelectedRecord]=useState({
        category:'-' as Category,
        tags:[] as string[],
        note:'',
        amount:'0'
    })

category是字符串类型,但是它只有'-' 或 '+'两种情况,所以可以缩小它的类型范围。用type Category='-' | '+'声明一个联合类型,表示category只有这两种情况,如果你写了其他字符串,就会报错。

3.

背景需要:一个输入框组件,包含一个label,label里边有一个文本和input。这个输入框我们在多个地方需要,并且样式一样。所以我们把这个封装成一个Input组件。

//Input.tsx
type Props={
    label: string,
    placeholder: string,
    value: string,
    onChange: (e: any)=>void
}
const Input: React.FC <Props> =(props)=>{
    return (
        <Label>
            <span>{props.label}</span>
            <input type='text' placeholder={props.placeholder}
            value={props.value} onChange={props.onChange}/>
        </Label>
    )
}
export default Input

因为使用它的不同地方,文本,placeholder等都是不同的,所以这些属性需要做成props等待父组件传进去。

定义一个Props类型。

在场景一使用:

const NoteSection: React.FC <Props> =(props)=>{
    const note=props.value
    const onChange=(e: any)=>{
        props.onChange(e.target.value)
    }
    return (
        <NoteWrapper>
            <Input label='备注:' placeholder='在这里写备注~'
                   value={note} onChange={onChange}>

            </Input>

        </NoteWrapper>
    )
}
export default NoteSection

在Input组件里传props。

可是在Input组件里这样接受props使代码很繁琐

3.1 改进:

既然我们传的props都是原本的input标签上的属性,如placeholder ,value ,onChange等。那我们可以在定义Props类型时,不一一展开写都有哪些属性,而是直接继承input标签的所有属性。

type Props={
    label: string,
}& React.InputHTMLAttributes<HTMLInputElement>

这么写的意思就是,props除了有个label属性外,还继承input元素的所有属性。input标签有哪些属性,props里也同样有。就代表现在已经存在了props.label,props.palceholder

在使用这些属性的时候,能不能再简写?不然有一百个属性,就要在input标签里写一百次。使用...rest语法

type Props={
    label: string,
}& React.InputHTMLAttributes<HTMLInputElement>
const Input: React.FC <Props> =(props)=>{
    const {label,children,...rest}=props  // 1
    return (
        <Label>
            <span>{label}</span>
            <input {...rest}/>          // 2
        </Label>
    )
}
export default Input

使用析构赋值,把props里边的所有属性命名成同名属性。props里除了label, children属性外,还有所有的input标签的属性,使用...rest语法,表示把props里剩余的其他属性展开拷贝过来。

使用的时候,直接在input标签里,用rest使用。外边的{}表示里边是JS代码,里边把rest对象展开

这样做的意义就是把props里的所有input标签自带的属性拷贝到真正的input标签上。避免了要把所有属性全部写一遍的做法。使代码看起来很简洁。这样写和上边原始的写法效果是等价的。

在某个父组件里使用Input组件时,还是照常传props,写法不变。

4. Omit

我想声明两个类型:

type RecordItem={
    category: '-' | '+',
    tagsId: number[],
    note: string,
    amount: string
}
type TimedRecord={
    category: '-' | '+',
    tagsId: number[],
    note: string,
    amount: string,
    createdAt: string
}

第二个只比第一个多了一个属性。为了不重复,可以有两种方法:

4.1 & 连接

type RecordItem={
    category: '-' | '+',
    tagsId: number[],
    note: string,
    amount: string
}
type TimedRecord=RecordItem & {
    createdAt: string
}

4.2 Omit 函数

type TimedRecord={
    category: '-' | '+',
    tagsId: number[],
    note: string,
    amount: string,
    createdAt: string
}
type RecordItem=Omit<TimedRecord,'createAt'>

先定义多的那个类型。用TS提供的一个函数:Omit 我不要这个类型里的某一项。(忽略)

如果不要多个属性:type RecordItem=Omit<TimedRecord,'createAt' | 'amount'>