Less is more
- 我们反对繁琐且不必要的各种模板
- redux的核心功能必须使用大量的模板
- 为了降低redux使用的复杂性,减少模板代码的使用,redux-toolkit( RTX )应运而生
redux-toolkit的基本使用
安装
yarn add @reduxjs/toolkit
创建slice
import {createSlice, PayloadAction} from '@reduxjs/toolkit'interface ProductDetailState {loading: boolean,error: string | null,data: any}const initialState: ProductDetailState = {loading: true,error: null,data: null}export const productDetailSlice = createSlice({name: 'productDetail',initialState,reducers: {fetchStart: (state) => {// return {...state, loading: true}state.loading = true},fetchSuccess: (state, action) => {state.loading = falsestate.data = action.payloadstate.error = null},fetchFail: (state, action: PayloadAction<string|null>) => {state.error = action.payloadstate.loading = false}}})
- reducers 将 action 和 reducer 结合在一起了
- 因为RTK使用了 immer 这个依赖,在 reducer 中可以直接修改 state
改用RTK的combineReducer
```tsx import { createStore,applyMiddleware } from ‘redux’ import languageReducer from ‘./language/languageReducer’ import recommendProductsReducer from ‘./recommendProducts/recommendProductsReducer’ import thunk from ‘redux-thunk’ import { actionLog } from ‘./middlewares/actionLog’
import { productDetailSlice } from ‘./productDetail/slice’ import { combineReducers } from ‘@reduxjs/toolkit’
const rootReducer = combineReducers({ language: languageReducer, recommendProducts: recommendProductsReducer, productDetail: productDetailSlice.reducer })
const store = createStore(rootReducer, applyMiddleware(thunk, actionLog))
export type RootState = ReturnType
<a name="zWFut"></a>#### 在组件中使用state和dispatch```tsx// ...import { useSelector, useDispatch } from "../../redux/hooks";import { productDetailSlice } from "../../redux/productDetail/slice";export const DetailPage: React.FC = () => {const { touristRouteId } = useParams();// const [loading, setLoading] = useState<boolean>(true);// const [product, setProduct] = useState<any>(null);// const [error, setError] = useState<string | null>(null);const loading = useSelector(state => state.productDetail.loading)const error = useSelector(state => state.productDetail.error)const product = useSelector(state => state.productDetail.data)const dispatch = useDispatch()useEffect(() => {const fetchData = async () => {dispatch(productDetailSlice.actions.fetchStart())try {const { data } = await axios.get(`/api/touristRoutes/${touristRouteId}`);dispatch(productDetailSlice.actions.fetchSuccess(data))} catch (error:any) {dispatch(productDetailSlice.actions.fetchFail(error.message))}};fetchData();}, []);// ...}
RTK中使用thunk处理异步请求
在RTK中使用thunk处理异步请求,必须使用 configureStore 和 createAsyncThunk
使用 configureStore 代替 createStore
import { createStore,applyMiddleware } from 'redux'import languageReducer from './language/languageReducer'import recommendProductsReducer from './recommendProducts/recommendProductsReducer'import thunk from 'redux-thunk'import { actionLog } from './middlewares/actionLog'import { productDetailSlice } from './productDetail/slice'import { combineReducers, configureStore } from '@reduxjs/toolkit'const rootReducer = combineReducers({language: languageReducer,recommendProducts: recommendProductsReducer,productDetail: productDetailSlice.reducer})// const store = createStore(rootReducer, applyMiddleware(thunk, actionLog))// 接受一个对象作为参数const store = configureStore({reducer: rootReducer,// getDefaultMiddleware获取默认中间件middleware: (getDefaultMiddleware) => [...getDefaultMiddleware(), actionLog],// 浏览器使用devTools插件可以查看redux状态devTools: true,})export type RootState = ReturnType<typeof store.getState>export type AppDispatch = typeof store.dispatchexport default store

使用 createAsyncThunk 定义异步action creator
import axios from 'axios';import {createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit'interface ProductDetailState {loading: boolean,error: string | null,data: any}const initialState: ProductDetailState = {loading: true,error: null,data: null}export const getProductDetail = createAsyncThunk('productDetailSlice/getProductDetail', // typeasync (touristRouteId: string, thunkAPI) => {thunkAPI.dispatch(productDetailSlice.actions.fetchStart())try {const { data } = await axios.get(`/api/touristRoutes/${touristRouteId}`);thunkAPI.dispatch(productDetailSlice.actions.fetchSuccess(data))} catch (error:any) {thunkAPI.dispatch(productDetailSlice.actions.fetchFail(error.message))}})export const productDetailSlice = createSlice({name: 'productDetail',initialState,reducers: {fetchStart: (state) => {// return {...state, loading: true}state.loading = true},fetchSuccess: (state, action) => {state.loading = falsestate.data = action.payloadstate.error = null},fetchFail: (state, action: PayloadAction<string|null>) => {state.error = action.payloadstate.loading = false}}})
在组件中 dispatch
useEffect(() => {const fetchData = async () => {dispatch(getProductDetail(touristRouteId))};fetchData();}, []);
使用 promise 生命周期
createAsyncThunk会返回 3 种状态,将这三种状态对应的操作写在 slice 的 extraReducers 中,在对应的 promise生命周期就会执行对应的操作, createAsyncThunk中只需要有返回值(promise), 不需要有dispatch操作
import axios from 'axios';import {createAsyncThunk, createSlice, PayloadAction} from '@reduxjs/toolkit'interface ProductDetailState {loading: boolean,error: string | null,data: any}const initialState: ProductDetailState = {loading: true,error: null,data: null}export const getProductDetail = createAsyncThunk('productDetailSlice/getProductDetail', // typeasync (touristRouteId: string, thunkAPI) => {const { data } = await axios.get(`/api/touristRoutes/${touristRouteId}`);return data})export const productDetailSlice = createSlice({name: 'productDetail',initialState,reducers: {// fetchStart: (state) => {// // return {...state, loading: true}// state.loading = true// },// fetchSuccess: (state, action) => {// state.loading = false// state.data = action.payload// state.error = null// },// fetchFail: (state, action: PayloadAction<string|null>) => {// state.error = action.payload// state.loading = false// }},extraReducers: {[getProductDetail.pending.type]: (state) => {// return {...state, loading: true}state.loading = true},[getProductDetail.fulfilled.type]: (state, action) => {state.loading = falsestate.data = action.payloadstate.error = null},[getProductDetail.rejected.type]: (state, action: PayloadAction<string|null>) => {state.error = action.payloadstate.loading = false}}})
