TypeScript学习笔记(十五)

77 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第7天,点击查看活动详情

前言

大家好呀,我是L同学。在上篇文章TypeScript学习笔记(十四)中,我们学习了TypeScript的相关知识点,包括非空断言、react路由的使用,包括useHistory的使用、useLocation的使用、useParams的使用的使用等相关知识点。在本篇文章中,我们将学习TypeScript的相关知识点,包括在ts项目中如何使用redux、useSelector的使用、RootState获取、reducer的使用、useDispatch的使用、事件对象的类型、redux thunk的使用等内容。

redux基本使用

在ts项目中如何使用redux呢?以todos案例为例。

安装依赖包。

yarn add redux react-redux redux-devtools-extension

新建文件 store/index.ts。

import { createStore } from 'redux'
import reducer from './reducers'
import { composeWithDevTools } from 'redux-devtools-extension'
const store = createStore(reducer, composeWithDevTools())

export default store

新建文件 store/reducers/index.ts。

import { combineReducers } from 'redux'
import todos from './todos'
const rootReducer = combineReducers({
  todos,
})
export default rootReducer

新建文件 store/reducers/todos.ts。

const initValue = [
  {
    id: 1,
    name: '吃饭',
    done: false,
  },
  {
    id: 2,
    name: '睡觉',
    done: true,
  },
  {
    id: 3,
    name: '打豆豆',
    done: false,
  },
]
export default function todos(state = initValue, action: any) {
  return state
}

index.tsx中。

import ReactDOM from 'react-dom'
import './index.css'
import App from './App'
import store from './store'
import { Provider } from 'react-redux'
ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
)

useSelector的使用

useSelector接收两个泛型参数:

  • 类型变量TState用于指定state的类型
  • TSelected用于指定返回值的类型
export function useSelector<TState = DefaultRootState, TSelected = unknown>(
    selector: (state: TState) => TSelected,
    equalityFn?: (left: TSelected, right: TSelected) => boolean
): TSelected;

useSelector的基本使用。

// 获取todos数据
const todos = useSelector<{ name: string }, string>((state) => state.name)

useSelector使用方式2,不指定泛型参数,直接指定state的类型。

const todos = useSelector((state: { name: string }) => state.name)

RootState获取

function fn(n1: number, n2:number):number {
  return n1 + n2
}

// 获取fn函数的类型
type Fn = typeof fn

// 获取Fn函数的返回值类型
type Res = ReturnType<Fn>

获取RootState的操作 store/index.tx

export type RootState = ReturnType<typeof store.getState>

useSelector的正确用法。

import { RootState } from '../store'

// 获取todos数据
const todos = useSelector((state: RootState) => state.todos)

reducer的使用

准备Action。

export function addTodo(name: string) {
  return {
    type: 'ADD_TODO',
    name,
  }
}

export function delTodo(id: number) {
  return {
    type: 'DEL_TODO',
    id,
  }
}

需要给action提供类型。

export type TodoAction =
  | {
      type: 'ADD_TODO'
      name: string
    }
  | {
      type: 'DEL_TODO'
      id: number
    }

export const addTodo = (name: string): TodoAction => {
  return {
    type: 'ADD_TODO',
    name
  }
}

export const delTodo = (id: number): TodoAction => {
  return {
    type: 'DEL_TODO',
    id
  }
}

在reducer中指定初始值的类型。

type TodosList = {
  id: number
  name: string
  done: boolean
}[]
const initValue: TodosList = []
export default function todos(state = initValue, action: any): TodosList {
  return state
}

指定Action的类型。

import { TodoAction } from '../actions/todos'

编写reducer。

import { TodoAction } from '../types'
export default function todos(
  state = initValue,
  action: TodoAction
): TodosList {
  if (action.type === 'ADD_TODO') {
    return [
      {
        id: Date.now(),
        name: action.name,
        done: false,
      },
      ...state,
    ]
  }
  if (action.type === 'DEL_TODO') {
    return state.filter((item) => item.id !== action.id)
  }
  return state
}

useDispatch的使用

useDispatch接收一个泛型参数用于指定Action的类型。

const dispatch = useDispatch()

<button onClick={() => dispatch(delTodo(item.id))}>x</button>

事件对象的类型

在使用事件对象时,需要指定事件对象的类型。有一个技巧是,在行内事件中,鼠标移动到e上面可以看到具体的事件对象类型。

image.png

const add = (e: React.KeyboardEvent<HTMLInputElement>) => {
  if (e.code === 'Enter') {
    dispatch(addTodo(name))
    setName('')
  }
}

redux thunk的使用

引入redux-thunk。

yarn add redux-thunk

import thunk from 'redux-thunk'
const store = createStore(reducer, composeWithDevTools(applyMiddleware(thunk)))

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

image.png

image.png ThunkAction类型的使用。

export type RootThunkAction = ThunkAction<void, RootState, unknown, TodoAction>

// 修改删除Action
export function delTodo(id: number): RootThunkAction {
  return (dispatch) => {
    setTimeout(() => {
      dispatch({
        type: 'DEL_TODO',
        id,
      })
    }, 1000)
  }
}