@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> ) }