前言
本文主要侧重于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)
总结 :
- useState本身是需要接收泛型参数的,由于有类型推断机制,这个泛型可以省略
- 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>
)
}
遇到错误:
解决方法 :
- 提前给初始值,便于类型推导
`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中报错:
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接收两个泛型参数
-
第一个泛型类型:TState的默认值是DefaultRootState(就是{})用于指定state的类型
-
第二个泛型类型: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