持续创作,加速成长!这是我参与「掘金日新计划 · 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上面可以看到具体的事件对象类型。
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的类型。
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)
}
}