react项目使用redux入门-7-使用@reduxjs/toolkit
@reduxjs/toolkit
场景:获取产品详情
@reduxjs/toolkit 依赖了redux、redux-thunk,所以使用toolkit就不需要额外下载redux,意味着可以再多个框架使用,但是并没有react-redux,所以仍然需要安装react-redux。依赖了redux-thunk,提供了createAsyncThunk方法,可以支持异步
-
安装依赖
npm i react-redux @reduxjs/toolkit --save
-
入口使用
react-redux
的Provider
组件包裹App
组件,并加载数据仓库store
:src/index.tsx
import React from 'react' import ReactDOM from 'react-dom' import { Provider } from 'react-redux' import App from './App' import store from 'redux/store.ts' ReactDOM.render( <React.StrictMode> <Providr store={store}> <App/> </Provider> </React.StrictMode> ,document.getElementById('root') )
-
store
数据封装- 新建目录:
src/redux
、src/redux/productDetail
- 新建文件:
src/redux/store.ts
、src/redux/hook.ts
、src/redux/productDetail.slice.ts
mkdir src/redux src/redux/productDetail touch src/redux/store.ts src/redux/productDetail/slice.ts
store.ts
import {configureStore} from '@reduxjs/toolkit' import {productDetailSlice} from './productDetail/slice' import languageReducer from './language/reducer' const rootReducer = combineReducers({ language: languageReducer, productDetail: productDetailSlice.reducer }) const store = configureStore({ reducer: rootReducer, middleware: getDefaultMiddleware => [...getDefaultMiddleware()] }) export type RootState = ReturnType<typeof store.getState> export default store
hook.ts
import { useSelector as useReduxSelector, TypedUseSelectorHook } from 'react-redux' import { RootState } from './store' export const useSelector: TypedUseSelectorHook<RootState> = useReduxSelector
productDetail/slice.ts
// createAsyncThunk: 方法返回一个函数型action:AsyncThunk<Returned, ThunkArg, {}> import { createSlice, PayloadAction, createAsyncThunk } from '@reduxjs/toolkit' import axios form 'axios' interface ProductDetailState = { loading: false, detail: any, error: null | string } const initialState:ProductDetailState = { loading: false, detail: {}, error: null } // createAsyncThunk 方法,返回一个 函数类型的 action export const getDetailAction = createAsyncThunk( 'productDetail/getDetailAction', // 异步action的内部命名空间的名称 async (id:string) => { // payloadCreator const { data } = await axios.get(`/product/${id}`) return data } ) export const productDetailSlice = createSlice({ name: 'productDetail', initialState, reducers:{}, extraReducers: { // js中[getDetailAction.pending],ts中[getDetailAction.pending.type]是ts需要类型 [getDetailAction.pending.type]: state => state.loading = true, [getDetailAction.fulfilled.type]: (state, action) => { state.loading = false state.detail = action.payload }, [getDetailAction.rejected.type]: (state, action: PayloadAction<null | string>) => { state.loading = false state.error = action.payload } } })
- 新建目录:
-
ProductDetail
组件中使用import React, { useEffect } from 'react' import { useParams } from 'react-router-dom' import { Spin } from 'antd' // 使用toolkit来进行数据请求 import { useSelector } from "redux/hooks"; // useSelector是react-redux中的useSelector再次封装过 import { useDispatch } from "react-redux"; import { getDetailAction } from "redux/productDetail/slice"; export const DetailView = () => { const { touristRouteId } = useParams<{ touristRouteId: string }>() // 请求数据 const dispatch = useDispatch() const loading = useSelector(({ productDetail }) => productDetail.loading) const error = useSelector(({ productDetail }) => productDetail.error) const detail = useSelector(state => state.productDetail.detail) useEffect(() => { dispatch(getDetailAction(touristRouteId)) }, []) return loading ? (<Spin tip='正在加载...' size='large'/>) : ( <div> {detail.title} </div> ) }