TypeScript在React中使用总结

403 阅读4分钟

前言

本文主要侧重于TypeScript在React 中的使用,而非TypeScript的基本使用,如果想要查看TS的基本类型可以使用在线TS工具

与Hooks的结合

在hooks中,并非全部钩子都与TS有强关联,比如useEffect就不依赖TS做类型定义,我们挑选比较常见的几个和TS强关联的钩子来看看。

useState

导入useState

通过ctrl+点击,查看格式

useState的类型声明如下

 /**
 * Returns a stateful value, and a function to update it.
 *
 * @version 16.8.0
 * @see https://reactjs.org/docs/hooks-reference.html#usestate
 */
function useState<S>(initialState: S | (() => S)): [S, Dispatch<SetStateAction<S>>];

标准用法-接收泛型参数

useState接收一个泛型参数,用于指定初始值的类型

// 使用泛型
const [name, setName] = useState<string>('张三')
const [age, setAge] = useState<number>(28)
const [isProgrammer, setIsProgrammer] = useState<boolean>(true)

// 如果你在set函数中的参数不符合声明的变量类型,程序会报错
<button onClick={() => setName(100)}>按钮</button>  // 报错

简便用法-使用类型推论省略泛型参数

在使用useState的时候,只要提供了初始值,typescript会自动根据初始值进行类型推断,因此useState的泛型参数可以省略

 const [name, setName] = useState('张三')
const [age, setAge] = useState(28)
const [isProgrammer, setIsProgrammer] = useState(true)

总结 :

  1. useState本身是需要接收泛型参数的,由于有类型推断机制,这个泛型可以省略
  2. useState的使用与js基本一致

useState 进阶用法

import { useEffect, useState } from 'react'
import axios from 'axios'

export default function App() {
  // 存放频道列表数据
  const [list, setList] = useState([])
  useEffect(() => {
    const fetchData = async () => {
      const res = await axios.get('xxxxx')
      setList(res.data.data.channels)
    }
    fetchData()
  }, [])
  return (
    <div>
      <ul>
        {list.map((item) => {
          return <li key={item.id}>{item.name}</li> {/*这里会报错:*/}
        })}
      </ul>
    </div>
  )
}

遇到错误:

image.png 解决方法 :

  1. 提前给初始值,便于类型推导
`const [list, setList] = useState([{id:1, name: 'test'}])`

2 补充泛型参数:any大法

`const [list, setList] = useState<any[]>([])`

3. 补充泛型参数:按接口返回值自定义类型

    type Channel = { id: number, name: string}[]

    const [list, setList] = useState<Channel[]>([])

useRef

react代码:

  const inputRef = useRef(null)
<input type="text" ref="inputRef" />
onClick = () => {
  console.log(inputRef.current.value)  
}

在TS中报错:

image.png

useRef的泛型参数

useRef 接收一个泛型参数,源码如下

/**
 * `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
 * (`initialValue`). The returned object will persist for the full lifetime of the component.
 *
 * Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
 * value around similar to how you’d use instance fields in classes.
 *
 * @version 16.8.0
 * @see https://reactjs.org/docs/hooks-reference.html#useref
 */
function useRef<T>(initialValue: T): MutableRefObject<T>;

interface MutableRefObject<T> {
    current: T;
}

useRef的泛型参数用于指定current属性的值的类型

解决方法 :

使用useRef操作DOM,需要明确指定所操作的DOM的具体的类型,否则current属性会是null 正确语法 :

 const inputRef = useRef<HTMLInputElement>(null)

useSelector

useSelector的基本使用

按之前的js的使用格式,它会报错

 // 获取todos数据
const todos = useSelector( state  => state.todos) // 这里会报错

useSelector格式

useSelector接收两个泛型参数

  1. 第一个泛型类型:TState的默认值是DefaultRootState(就是{})用于指定state的类型

  2. 第二个泛型类型:TSelected用于指定返回值的类型

     export function useSelector<TState = DefaultRootState, TSelected = unknown>(
         selector: (state: TState) => TSelected,
         equalityFn?: (left: TSelected, right: TSelected) => boolean
     ): TSelected;
    

使用 : 方式1: 指定泛型类型

 const todos = useSelector<{todos:[]} ,{id:number,name:string,isDone:boolean}[]>(state => state.todos)
console.log(todos)

方式2: 直接指定回调函数的形参类型

const todos = useSelector((state: {todos:{id:number, name:string,isDone:boolean}[]})  => state.todos)

注意,我们没有指定返回值的类型。因为它可以自己推论出来

Redux相关

对于action的定义,我们可以使用组合类型进行定义

  export type ActionType ={type:'update',payload:number} | {type:'DELETE' ,payload:number} |{type:'ADD',payload:{name:string,isDone:boolean}} |{type:"INIT_STATE",payload:TodoType}

对于reducer中state的定义

export  type  TodoType={
    id:number,
    name:string,
    isDone:boolean
}[]

这样就在使用时直接导入就行,以下是reducer的代码:

 import { ActionType, TodoType } from ".."

const initValue:TodoType = [
]

export  default  function todos (state=initValue,action:ActionType):TodoType{

switch (action.type) {
    case 'update':
        return state.map(item=>{
            if(item.id===action.payload){
             return {
                   ...item,
                isDone:!item.isDone
             }
            }else{
             return   item
            }
        })
    case 'DELETE':
        return state.filter(item=>item.id!==action.payload)                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                          
    case 'ADD' :
          return [...state,{...action.payload,id:state.length+1}]
    case 'INIT_STATE' :
        return action.payload
    default:
        return state
}

}

在action文件中直接导入使用 ,代码如下 :

import { ActionType } from ".."
export  const  updateState=(id:number):ActionType=>{
  return {
      type:'update',
      payload:id
  }
}
export  const  addState=(name:string):ActionType=>{
  return {
      type:'ADD',
      payload:{
          name:name,
          isDone:false
      }
  }
}
export  const  deleteState=(id:number):ActionType=>{
  return {
      type:'DELETE',
      payload:id
  }
}

ThunkAction类型的使用

thunk类型的变更,使用了thunk之后,返回的Action类型不再是对象,而是函数类型的Action,因此需要修改Action的类型。ThunkAction类型的使用

参考文档

// 类型参数1:ReturnType 用于指定函数的返回值类型 void
// 类型参数2: 指定 TodoType 的类型 也就是state的类型
// 类型参数3: 指定额外的参数类型,一般为unkonwn或者any
// 类型参数4: 用于指定dispatch的Action类型 所有的action类型
// 修改删除Action
export function delTodo(id: number): ThunkAction<void, TodoType, unknown, ActionType> {
  return (dispatch) => {
    setTimeout(() => {
      dispatch({
        type: 'DEL_TODO',
        id,
      })
    }, 1000)
  }
}

redux-thunk版本bug

在redux-thunk@2.4.0新版中,使用dispatch的时候,会丢失提示,需要降级到2.3.0版本 命令 : npm i redux-thunk@2.3.0