本文为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';
// xxxfunction 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中的办法、异步恳求的办法以及所有涉及bannerrecommend数据的当地就可以去消费。

@/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;