05-react-ts

React 和 TS 集成: 存储数据 / 状态有关的 Hook 函数组件接口 的位置,这些地方最需要数据类型校验

1. Vite创建项目

Vite 是一个框架无关的前端工具链工具,可以快速创建一个 react + ts 的工程化环境出来,可以基于它做语法学习

Viteopen in new window

npm create vite@latest react-ts -- --template react-ts

# 安装依赖
npm i

# 运行项目
npm run dev

2. Hooks与TS

1. 简单场景

简单场景下:可以使用 TS 的自动推断机制,不用特殊编写类型注解,运行良好

const [val, toggle] = React.useState(false)

// `val` 被自动推断为布尔类型
// `toggle()` 调用时只能传入布尔类型

2. 复杂场景

复杂数据类型:useState 支持通过 泛型参数 指定初始参数类型以及 setter() 的入参类型

type User = {
  name: string
  age: number
}
const [user, setUser] = React.useState<User>({
  name: 'jack',
  age: 18
})
// 执行 setUser
setUser(newUser)
// 这里 newUser 对象只能是 User 类型

3. 没有具体默认值

实际开发时,有时 useState 的初始值可能为 null 或 undefined,按照泛型的写法是不能通过类型校验的,此时可以通过完整的类型联合 null 或 undefined 类型即可

type User = {
    name: String
    age: Number
}
// 类型错误,因为 null 并不能分配给 User 类型
const [user, setUser] = useState<User>(null)

// 既可以在初始值设置为 null,同时满足 setter() setUser 的参数可以是具体的 User 类型
const [user, setUser] = useState<User | null>(null)

3. useRef

在 TS 的环境下,useRef 函数返回一个 只读可变 的引用

  1. 只读的场景常见于获取真实 dom
  2. 可变的场景,常见于缓存一些数据,不跟随组件渲染

1. 获取dom

获取 DOM 时,通过泛型参数指定具体的 DOM 元素类型即可

function Foo() {
    // 尽可能提供一个具体的 dom type,在用 dom 属性时有更明确的提示
    // divRef 的类型为 RefObject<HTMLDivElement>
    // Argument type null is not assignable to parameter type HTMLDivElement
    const inputRef = useRef<HTMLDivElement>(null)

    useEffect(() => {
        inputRef.current.focus()
    })

    return <div ref={inputRef}>etc</div>
}




 


 




如果你可以确保 divRef.current 不是null,也可以在传入初始值的位置

// 添加非 null 标记
const divRef = useRef<HTMLDivElement>(null!)
// 不再需要检查 `divRef.current` 是否为 null
doSomethingWith(divRef.current)

 


2. 稳定引用存储器

当做为可变存储容器使用时,可以通过 泛型参数 指定容器存入的数据类型。存入实际内容时,通常把 null 作为初始值,所以依旧可以通过联合类型做指定

interface User {
  age: number
}

function App(){
  const timerRef = useRef<number | undefined>(undefined)
  const userRes = useRef<User | null> (null)

  useEffect(() => {
    timerRef.current = window.setInterval(() => {
      console.log('测试')
    }, 1000)

    return () => clearInterval(timerRef.current)
  })

  return <div> this is app</div>
}





 







 




4. Component与TS

1. Component与TS

props 作为 React 组件的参数入口,添加了类型之后可以限制参数输入以及在使用 props 有良好的类型提示

1. interface接口

interface Props {
    className: string
}

export const Button = (props: Props) => {
    const {className} = props
    return <button className={className}>Test</button>
}
 



 



2. 自定义类型Type

type Props = {
    className: string
}

export const Button = (props: Props) => {
    const {className} = props
    return <button className={className}>Test</button>
}
 



 



2. children属性添加类型

  • children 属性和 props 中其他的属性不同,它是 React 系统中内置的,其它属性可以自由控制其类型,children 属性的类型最好由 React 内置的类型提供,兼容多种类型
  • 说明:React.ReactNode 是一个React 内置的联合类型,包括 React.ReactElementstringnumberReact.ReactFragmentReact.ReactPortalbooleannullundefined
type Props = {
    children: React.ReactNode
}

export const Button = (props: Props) => {
    const {children} = props
    return <button>{children}</button>
}

 






3. 事件类型

type Props = {
    onGetMsg?: (msg: string) => void
}

function Son(props: Props) {
    const {onGetMsg} = props
    const clickHandler = () => {
        onGetMsg?.('this is msg')
    }
    return <button onClick={clickHandler}>sendMsg</button>
}

function App() {
    const getMsgHandler = (msg: string) => {
        console.log(msg)
    }
    return (
        <>
            <Son onGetMsg={(msg) => console.log(msg)}/>
            <Son onGetMsg={getMsgHandler}/>
        </>
    )
}

export default App

 





 

















4. 事件handle类型

为事件回调添加类型约束,需要使用 React 内置的泛型函数来做。eg:最常见的鼠标点击事件和表单输入事件:

function App() {
    const changeHandler: React.ChangeEventHandler<HTMLInputElement> = (e) => {
        console.log(e.target.value)
    }

    const clickHandler: React.MouseEventHandler<HTMLButtonElement> = (e) => {
        console.log(e.target)
    }

    return (
        <>
            <input type="text" onChange={changeHandler}/>
            <button onClick={clickHandler}> click me!</button>
        </>
    )
}