reduxjs/toolkit基本使用案列

775 阅读3分钟

reduxjs/toolkit基本使用案列

更多介绍见官网

安装

npm install @reduxjs/toolkit react-redux @types/react-redux // redux及工具包
npm install @types/redux-logger redux-logger //日志包
npm install  redux-persist //数据缓存

使用

1、创建slice

同步使用 counterSlice.ts

import { createSlice, PayloadAction } from '@reduxjs/toolkit'

export interface CounterState {
  value: number
  title: string
}
const initialState: CounterState = {
  value: 0,
  title: 'redux toolkit pre',
}

// 创建一个 Slice
export const counterSlice = createSlice({
  name: 'counter',
  initialState,
  // 定义 reducers 并生成关联的操作
  reducers: {
    // 定义一个加的方法
    increment: state => {
      state.value += 1
    },
    // 定义一个减的方法
    decrement: state => {
      state.value -= 1
    },
    incrementByAmount: (state, action: PayloadAction<{ vaule: number }>) => {
      // action 里面有 type 和 payload 两个属性,所有的传参都在payload里面
      // console.log(action)
      // {"type": "counter/incrementByAmount","payload": {"vaule": 2}}
      state.value += action.payload.vaule
    },
  },
})
// 导出加减的方法
export const { increment, decrement, incrementByAmount } = counterSlice.actions

// 默认导出
export default counterSlice.reducer

异步使用 movieSlice.ts

import {
  createAsyncThunk,
  createSlice,
  Draft,
  PayloadAction,
} from '@reduxjs/toolkit'

// 引入其他 slice 生成的 action
import { increment } from './counterSlice'

export interface MovieState {
  list: any[]
  totals: number
}
const initialState: MovieState = {
  list: [],
  totals: 0,
}

// 请求电影列表
const getMovieListApi = () =>
  fetch(
    'https://pcw-api.iqiyi.com/search/recommend/list?channel_id=1&data_type=1&mode=24&page_id=1&ret_num=48',
  ).then(res => res.json())

// thunk函数允许执行异步逻辑, 通常用于发出异步请求。
// createAsyncThunk 创建一个异步action,方法触发的时候会有三种状态:
// pending(进行中)、fulfilled(成功)、rejected(失败)
export const getMovieData = createAsyncThunk('movie/getMovie', async () => {
  const res = await getMovieListApi()
  return res
})

// 创建一个 Slice
export const movieSlice = createSlice({
  name: 'movie',
  initialState,
  reducers: {
    // 数据请求完触发
    loadDataEnd: (
      state: Draft<MovieState>,
      action: PayloadAction<{ list: any[] }>,
    ) => {
      state.list = action.payload.list
      state.totals = action.payload.list.length
    },
  },

  // extraReducers 字段让 slice 处理在别处定义的 actions,
  // 包括由 createAsyncThunk 或其他slice生成的actions。
  extraReducers(builder) {
                                      
    // 处理其他 slice 生成的 actions
    builder.addCase(increment, state => {
      // increment方法触发时的处理
      state.list.push({ name: '加' })
      state.totals += 1
    })

    // 处理createAsyncThunk 生成的 actions
    builder
      .addCase(getMovieData.pending, state => {
        console.log('🚀 ~ 进行中!')
      })
      .addCase(getMovieData.fulfilled, (state, { payload }) => {
        console.log('🚀 ~ fulfilled', payload)
        state.list = payload.data.list
        state.totals = payload.data.list.length
      })
      .addCase(getMovieData.rejected, (state, err) => {
        console.log('🚀 ~ rejected', err)
      })
  },
})

// 导出方法
export const { loadDataEnd } = movieSlice.actions

// 默认导出
export default movieSlice.reducer

2、配置store

store.ts

import { configureStore, combineReducers } from '@reduxjs/toolkit'
import logger from 'redux-logger'
import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'
// persistStore 为redux-persist内置的状态管理仓库;persistReducer 为内置的切片管理;
import { persistStore, persistReducer } from 'redux-persist'
// import storage from 'redux-persist/lib/storage' // 本地存储
import storage from 'redux-persist/lib/storage/session' // 会话存储

// 多个Slice的引入;
import counterSlice from './modules/counterSlice'
import movieSlice from './modules/movieSlice'

// 配置要存储的Slice;
const persistConfig = {
  key: 'root', // key是放入localStorage中的key
  storage,
  // whitelist: ['language'], // 需要缓存的数据  默认缓存所有
  // blacklist: ['navigation'], // navigation不会被存入缓存中,其他会,适用于少部分数据需要实时更新
}

// 合并多个Slice
const rootReducer = combineReducers({
  counter: counterSlice,
  movie: movieSlice,
})
const myPersistReducer = persistReducer(persistConfig, rootReducer)
// configureStore创建一个redux数据
const store = configureStore({
  reducer: myPersistReducer,
  // 配置中间键
  middleware: getDefaultMiddleware =>
    getDefaultMiddleware({ serializableCheck: false }).concat(logger),
  devTools: true,
})

export type RootState = ReturnType<typeof store.getState>
export type AppDispatch = typeof store.dispatch

// 二次封装:对useDispatch,useSelector进行封装,解决每次使用都要导入RootState,AppDispatch
export const useAppDispatch = () => useDispatch<AppDispatch>()
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector

export const persistor = persistStore(store)
export default store

3、注入store到react入口文件

import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import { ConfigProvider } from 'antd'
import zh_CN from 'antd/lib/locale/zh_CN'
import { Provider } from 'react-redux'
import store, { persistor } from '@/store'
// PersistGate的作用是向下分发persistStore对象;
import { PersistGate } from 'redux-persist/lib/integration/react'
import '@/config/i18n'
import App from '@/App'

const root = ReactDOM.createRoot(document.getElementById('root') as HTMLElement)
root.render(
  <React.StrictMode>
    <BrowserRouter>
      <Provider store={store}>
        <PersistGate loading={null} persistor={persistor}>
          <ConfigProvider locale={zh_CN}>
            <App />
          </ConfigProvider>
        </PersistGate>
      </Provider>
    </BrowserRouter>
  </React.StrictMode>,
)

4、页面中使用

test.tsx

import React from 'react'
// 引入对应的方法
import {
  increment,
  decrement,
  incrementByAmount,
} from '@/store/modules/counterSlice'
import { getMovieData } from '@/store/modules/movieSlice'

// 如何操作store
// 方式一、比较繁琐,每个文件都得这样引入
// 引入相关的hooks
import { useSelector, useDispatch } from 'react-redux'
// 引入store类型
import type { RootState, AppDispatch } from '@/store'

// 方式二、比较简单
import { useAppDispatch, useAppSelector } from '@/store'

const ReduxToolkitTest: React.FC = () => {
  // 使用:方式一
  // // 通过useSelector直接拿到store中定义的counter
  // const { value } = useSelector((store: RootState) => store.counter)
  // // 通过useSelector直接拿到store中定义的movie
  // const { list } = useSelector((store: RootState) => store.movie)
  // // 通过useDispatch 派发事件
  // const dispatch = useDispatch<AppDispatch>()

  // 使用:方式二
  // 通过useSelector直接拿到store中定义的counter
  const { value } = useAppSelector(store => store.counter)
  // 通过useSelector直接拿到store中定义的movie
  const { list } = useAppSelector(store => store.movie)
  // 通过useDispatch 派发事件
  const dispatch = useAppDispatch()

  return (
    <div className="App">
      <header className="App-header">
        <p>{value}</p>
        <button
          onClick={() => {
            dispatch(increment())
          }}
        >
          加
        </button>
        <button
          onClick={() => {
            dispatch(decrement())
          }}
        >
          减
        </button>
        <button
          onClick={() => {
            dispatch(incrementByAmount({ vaule: 2 }))
          }}
        >
          加2
        </button>
      </header>
      <hr />
      <main>
        <h1>电影列表</h1>
        <button
          onClick={() => {
            dispatch(getMovieData())
          }}
        >
          获取数据
        </button>
        <ul>
          {list.map((item, index) => {
            return <li key={index}> {item.name}</li>
          })}
        </ul>
      </main>
    </div>
  )
}

export default ReduxToolkitTest