本文为react+ts项目中运用rtk的经历贴,ts类型方面踩了一些坑,比较浪费时间。这儿记载一下便利遇到相同场景的朋友以及自己后续运用时快速cv
根store界说
根store的装备,src/store/index.ts
:
// recommendReducer即为recommend路由组件(一个需求建立子store的组件)的reducer
import recommendReducer from '../application/Recommend/store';
import { configureStore } from '@reduxjs/toolkit';
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux';
const store = configureStore({
reducer: {
recommend: recommendReducer,
},
});
export type RootState = ReturnType<typeof store.getState>;
export type AppDispatch = typeof store.dispatch;
// 运用 useAppDispatch/useAppSelector 替代 useDispatch/useSelector 提供ts类型支撑
// 消费组件中运用useAppDispatch/useAppSelector与useDispatch/useSelector在运行时完全没有差异
// 如下,useAppDispatch/useAppSelector仅仅对useDispatch/useSelector进行了单纯的类型标示而已
type DispatchFunc = () => AppDispatch;
export const useAppDispatch: DispatchFunc = useDispatch;
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
export default store;
根组件中通过context传递根store,App.tsx
:
import { Provider } from 'react-redux';
import store from './store';
// xxx
function App() {
return (
<Provider store={store}>
{/*xxx*/}
</Provider>
);
}
export default App;
子store界说
目录结构参考
建立子store的组件目录结构:
application/ --路由组件文件夹
└── recommend/
├── index.tsx
└── store/ --recommend组件所属子store
└── index.ts
子store界说逻辑
先疏忽类型界说,理顺js逻辑,@/application/recommend/store/index.ts
:
import {
TdefaultState,
TBannerList,
TRecommendList,
} from '../../../types/recommend'; // 类型相关,暂时疏忽
import { createSlice, createAsyncThunk, PayloadAction } from '@reduxjs/toolkit';
import {
getBannerRequest,
getRecommendListRequest,
} from '../../../api/request'; // 获取异步数据的恳求api
// 子store的初始数据
const defaultState: TdefaultState = {
bannerList: [],
recommendList: [],
};
// 涉及异步恳求的action函数用createAsyncThunk来创立
// 第一个参数为任意字符串,标识效果,可能在devtool中有所帮助
// 第二个参数即为异步函数,函数回来值将作为下面创立的子store.extraReducers的钩子函数中的action.payload(然后用来修正store中的state)
export const getBannerList = createAsyncThunk('changeBannerList', async () => {
try {
const data = await getBannerRequest();
return data.banners;
} catch (e) {
console.log(e, '轮播图数据传输错误');
}
});
export const getRecommendList = createAsyncThunk(
'changeRecommendList',
async () => {
try {
const data = await getRecommendListRequest();
return data.result;
} catch (e) {
console.log(e, '引荐歌单数据传输错误');
}
},
);
// 子store用createSlice来创立
const recommendSlice = createSlice({
name: 'recommend', // name为标识效果的字符串
initialState: defaultState, // initialState即为状况初始值
// reducers里即为修正state的同步操作,这儿的state即为这个小store本身的state,便是initialState
// reducers里的函数最终的意图自然是露出给外面去调用的,所以最终需求export
// 上面如getBannerList这种涉及异步操作的修正state的函数本质也是为了对外露出然后被调用的,所以直接export即可
reducers: {
changeBannerList(state, action: PayloadAction<TBannerList>) {
state.bannerList = action.payload;
},
changeRecommendList(state, action: PayloadAction<TRecommendList>) {
state.recommendList = action.payload;
},
},
// 上面的getBannerList/getRecommendList与此store是无相关的,extraReducers便是将异步函数与子store作联络
// 联络方式:[<异步函数名>.<异步操作所回来的promise状况>.type]: <对应状况履行的函数回调>
extraReducers: {
[getRecommendList.fulfilled.type]: (state, action) => { // state即为子store的state(initialState);action.payload即为异步函数的回来值
state.recommendList = action.payload;
},
[getBannerList.fulfilled.type]: (state, action) => {
state.bannerList = action.payload;
},
},
});
export const { changeBannerList, changeRecommendList } = recommendSlice.actions; // 从recommendSlice.actions中露出同步action办法(reducers中的办法)
export default recommendSlice.reducer; // 默认导出recommendSlice.reducer,这个即为根store所需的子store
其实类型相关就与redux关系不大了,上面唯一与redux相关的类型便是给reducers里的同步办法增加了一个PayloadAction
类型,其泛型对应action.payload的类型。
项目类型界说实践
简略记载一下类型界说:
recommend
模块所需的数据对应的类型界说存放在@/types/recommend.ts
中,@/types/recommend.ts
:
// banner目标的类型界说
export interface IBannerItemBase {
imageUrl: string; // 当下事务所有必要的属性
}
export interface IBannerItem extends IBannerItemBase {} // 留作未来拓宽
// recommend数据目标与banner类似
export interface IRecommendItemBase {
id: number;
picUrl: string;
playCount: number;
name: string;
}
export interface IRecommendItem extends IRecommendItemBase {}
// (借助上面的目标类型)完成banner数组的类型
export type TBannerList = Array<IBannerItem> | [];
export type TRecommendList = Array<IRecommendItem> | []; // recommend数组类型
// (借助上面的数组类型)最终完成store的state的完整类型
export type TdefaultState = {
bannerList: TBannerList;
recommendList: TRecommendList;
};
类型的界说都是由局部到全体,先完成局部小类型,慢慢组装成杂乱变量的完整的类型界说
有了上面关于数据类型的界说,store中的办法、异步恳求的办法以及所有涉及banner
与recommend
数据的当地就可以去消费。
如@/types/request.ts
中去界说异步恳求的呼应体结构,@/types/request.ts
:
// 一个异步恳求接口对应一个呼应体结构(接口设计时字段及结构可能不统一),一个呼应体结构对应一个interface接口界说
export interface IBannerRequestStruct<TData = never> {
code: number;
banners: TData;
}
export interface IRecommendRequestStruct<TData = never> {
code: number;
result: TData;
}
在@/api/request.ts
中去界说真正的异步恳求办法一起消费上面界说的类型:
import { axiosInstance } from './config'; // 导入axios实例
import {
IRecommendRequestStruct,
IBannerRequestStruct,
} from '../types/request';
import { TBannerList, TRecommendList } from '../types/recommend';
// 获取banner数据
// Promise的泛型即为promise实例在fullfilled状况时的value,即为await解构得到的值,这儿即为IBannerRequestStruct<TBannerList>,也便是呼应体的类型
export const getBannerRequest: () => Promise<
IBannerRequestStruct<TBannerList>
> = () => {
return axiosInstance.get('/banner');
};
// 获取引荐列表
export const getRecommendListRequest: () => Promise<
IRecommendRequestStruct<TRecommendList>
> = () => {
return axiosInstance.get('/personalized');
};
组件中消费
经过上面的装备,整个store,包含根store以及子store就现已界说好了,最终便是如何在组件中进行store中状况的消费,@/application/Recommend/index.tsx
:
import { useAppDispatch, useAppSelector } from '../../store'; // ts中需求运用useAppDispatch、useAppSelector
import { RootState } from '../../store'; // 调用useAppSelector时给根state标示类型运用
import { getBannerList, getRecommendList } from './store';
function Recommend(): JSX.Element {
const dispatch = useAppDispatch(); // 运用useAppDispatch获取dispatch函数
const bannerList = useAppSelector(
(state: RootState) => state.recommend.bannerList,
);
const recommendList = useAppSelector(
(state: RootState) => state.recommend.recommendList,
);
useEffect(() => {
dispatch(getBannerList());
dispatch(getRecommendList());
}, []);
return (
<div>
<Slider bannerList={bannerList}></Slider>
<RecommendList recommendList={recommendList} />
</div>
);
}
export default Recommend;