概述
redux-toolkit 是redux封裝的工具包,底層還是redux,提供了configureStore、createSlice等方法更簡潔的編寫方式,如需瞭解更多redux-toolkit
API
名稱 | 用途 |
---|---|
configureStore | 創建store實例 |
createSlice | 創建切片,相當於模塊劃分,包含state,reducer等 |
useDispatch | 用於分發任務,reducer處理state |
useSelector | 接受一個函數,用於取出state |
createAction | createAction 接受一個 action 類型字符串作爲參數,並返回一個使用該類型字符串的 action creator 函數。 |
createReducer | 創建reducer ,接受state,和定義的action |
createAsyncThunk | 創建一個異步函數生成一個promise,可以利用這個promise狀態的改變,執行一些生命週期函數 |
createSelector | 創建類似 react memo 的記憶選擇器,來達到性能優化 |
- configureStore configureStore 主要幫助我們創建標準 redux 的函數抽象,爲 store 添加默認配置以獲得更好的開發體驗。configureStore 方法提供 5 個參數:reducer 用來配置我們的 reducer 函數。如果他是一個函數,它將直接用作 store 的 root reducer。如果它是 slices 的對象,例如 {users : usersReducer, posts : postsReducer}, configureStore 將通過將此對象傳遞給 ReduxcombineReducers 函數,它將自動的創建 root reducer。 下面是一段 configureStore 的示例代碼:
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit';
import counterReducer from '../features/counter/counterSlice';
import todoReducer from '../features/todo/todoSlice'
import reducer from '../reduxTodo/reducer'
export const store = configureStore({
reducer: {
counter: counterReducer,
todoReducer: reducer,
todo: todoReducer
},
})
middleware middleware 它提供我們中間件的配置,如果未提供,configureStore 將調用 getDefaultMiddleware 並使用它返回的中間件函數數組。
如果我們希望添加或自定義默認中間件,我們可以傳遞一個回調函數,該函數將接收 getDefaultMiddleware 作爲其參數,並返回一箇中間件數組。
示例代碼如下:
import logger from 'redux-logger'
const store = configureStore({
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
})
getDefaultMiddleware doc 這裏可以查看默認配置都有什麼中間件。
devTools 如果它是一個布爾值,它將用於 configureStore 是否應自動啓用對 Redux DevTools 瀏覽器擴展的支持。
如果它是一個對象,則將啓用 DevTools 擴展,並將選項對象傳遞給 composeWithDevtools()。 有關可用的特定選項的列表,請參閱 EnhancerOptions 的 DevTools 擴展文檔。
preloadedState preloadedState 很好理解,就是傳遞給 Redux createStore 函數的初始狀態值。
enhancers 用於增加 Redux store 的能力。
如果定義爲數組,這些將傳遞給 Redux compose 函數,組合的增強器將傳遞給 createStore。
如果定義爲回調函數,它將使用沒有 DevTools 擴展的現有增強器數組(當前爲 applyMiddleware)調用,並應返回一個新的增強器數組。 這主要適用於需要在 applyMiddleware 前面添加 store 增強器的情況,例如 redux-first-router 或 redux-offline。
import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'
import logger from 'redux-logger'
import { reduxBatch } from '@manaflair/redux-batch'
import counterReducer from '../features/counter/counterSlice'
import todoReducer, {Item} from '../features/todo/todoSlice'
import reducer from '../reduxTodo/reducer'
const preloadedState: Item[] = [
{
id: '5',
text: 'redux',
state: 'done'
},
{
id: '7',
text: 'toolkit',
state: 'todo'
}
]
export const store = configureStore({
reducer: {
counter: counterReducer,
todoReducer: reducer,
todo: todoReducer
},
middleware: (getDefaultMiddleware) => getDefaultMiddleware().concat(logger),
devTools: process.env.NODE_ENV !== 'production',
preloadedState: {
todo: {
list: preloadedState
}
},
enhancers: [reduxBatch],
})
- createReducer 一個簡化創建 Redux slice 函數的實用程序。 它在內部使用 Immer 通過在 slice 中編寫“可變”代碼來大大簡化不可變的更新邏輯,並支持將特定的動作類型直接映射到 case reducer 函數,這些函數將在調度該動作時更新狀態。
示例代碼如下:
import { createAction, createReducer } from '@reduxjs/toolkit'
const increment = createAction('counter/increment')
const decrement = createAction('counter/decrement')
const incrementByAmount = createAction('counter/incrementByAmount')
const initialState = { value: 0 }
const counterReducer = createReducer(initialState, (builder) => {
builder
.addCase(increment, (state, action) => {
state.value++
})
.addCase(decrement, (state, action) => {
state.value--
})
.addCase(incrementByAmount, (state, action) => {
state.value += action.payload
})
})
這裏就不展開講解 createReducer ,大多數情況我們都會 slice 形式創建標準的 reducer 函數。詳細內容可以查看官方文檔 createReducer。
- createAction 用於定義 Redux action type 和創建者的輔助函數。
在 Redux 中定義 action 的常用方法是分別聲明一個 action 類型常量和一個用於構造該類型 action 的動作創建者函數。
import { createAction } from '@reduxjs/toolkit'
const increment = createAction('counter/increment')
let action = increment()
// { type: 'counter/increment' }
action = increment(3)
// returns { type: 'counter/increment', payload: 3 }
console.log(increment.toString())
// 'counter/increment'
console.log(`The action type is: ${increment}`)
// 'The action type is: counter/increment'
大多數情況我們都會 slice 形式創建標準的 action 函數。詳細內容可以查看官方文檔 createAction。
3、useDispath,useAppSelector
import { useDispatch,useSelector,TypedUseSelectorHook } from 'react-redux';
import type { RootState,AppDispatch } from './index';
export const useAppSelector:TypedUseSelectorHook<RootState>=useSelector
export const useAppDispatch=()=>useDispatch<AppDispatch>();
//在頁面使用,取出state的值
const schemaTree = useAppSelector((state: RootState) => state.schemaTree)
//dispath操作
import React from 'react'
import { useDispatch } from 'react-redux'
export const CounterComponent = ({ value }) => {
const dispatch = useDispatch()
return (
<div>
<span>{value}</span>
<button onClick={() => dispatch({ type: 'increment-counter' })}>
Increment counter
</button>
</div>
)
}
- createSlice createSlice 是編寫 Redux 邏輯的標準方法。
一個接受初始狀態、reducer 函數對象和“切片名稱”的函數,並自動生成與 reducer 和狀態對應的動作創建者和動作類型。
示例代碼如下:
import {createSlice, PayloadAction} from '@reduxjs/toolkit'
export interface TodoState {
list: Item[]
}
export interface Item {
id: string,
text: string,
state: string
}
const todos: Item[] = [
{
id: '1',
text: 'react',
state: 'done'
},
{
id: '2',
text: 'vue',
state: 'todo'
},
{
id: '3',
text: 'angular',
state: 'todo'
}
]
const initialState: TodoState = {
list: todos
}
export const todoSlice = createSlice({
name: 'todoSlice',
initialState,
reducers: {
add: (state, action: PayloadAction<Item>) => {
state.list.push(action.payload)
},
remove: (state, action: PayloadAction<string>) => {
const index = state.list.findIndex(item => item.id === action.payload)
state.list.splice(index, 1)
},
toggle: (state, action: PayloadAction<string>) => {
const current = state.list.filter(item => item.id === action.payload)[0]
current.state = current.state === 'todo' ? 'done' : 'todo'
}
}
})
export const {add, remove, toggle} = todoSlice.actions
export default todoSlice.reducer
大多數情況下我們都會使用 createSlice 來編寫 redux 邏輯。
- createAsyncThunk 幫助我們編寫異步邏輯,例如接口調用。
一個接受 Redux 操作類型字符串的函數和一個返回 promise 的回調函數。它根據您傳入的操作類型前綴生成 promise 生命週期操作類型,並返回一個 thunk 操作創建者,它將運行 promise 回調並根據返回的 promise 調度生命週期操作。
這抽象了處理異步請求生命週期的標準方法。
示例代碼如下:
import { createAsyncThunk, createSlice, PayloadAction } from '@reduxjs/toolkit';
import { RootState, AppThunk } from '../../app/store';
import { fetchCount } from './counterAPI';
export interface CounterState {
value: number;
status: 'idle' | 'loading' | 'failed';
}
const initialState: CounterState = {
value: 0,
status: 'idle',
};
export const incrementAsync = createAsyncThunk(
'counter/fetchCount',
async (amount: number) => {
const response = await fetchCount(amount);
return response.data;
}
);
export const counterSlice = createSlice({
name: 'counter',
initialState,
reducers: {
increment: (state) => {
state.value += 1;
},
decrement: (state) => {
state.value -= 1;
},
incrementByAmount: (state, action: PayloadAction<number>) => {
state.value += action.payload;
},
},
extraReducers: (builder) => {
builder
.addCase(incrementAsync.pending, (state) => {
state.status = 'loading';
})
.addCase(incrementAsync.fulfilled, (state, action) => {
state.status = 'idle';
state.value += action.payload;
});
},
});
export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export const selectCount = (state: RootState) => state.counter.value;
export const incrementIfOdd = (amount: number): AppThunk => (
dispatch,
getState
) => {
const currentValue = selectCount(getState());
if (currentValue % 2 === 1) {
dispatch(incrementByAmount(amount));
}
};
export default counterSlice.reducer
更多使用方法和詳細的內容可以查看官方文檔 createAsyncThunk
- createSelector 可以幫助我們創建類似 react memo 的記憶選擇器,來達到性能優化。
它通過 Reselect 庫中的 createSelector 方法來實現。
示例代碼如下:
import React from 'react'
import { useSelector } from 'react-redux'
import { createSelector } from 'reselect'
const selectNumCompletedTodos = createSelector(
(state) => state.todos,
(todos) => todos.filter((todo) => todo.completed).length
)
export const CompletedTodosCounter = () => {
const numCompletedTodos = useSelector(selectNumCompletedTodos)
return <div>{numCompletedTodos}</div>
}
export const App = () => {
return (
<>
<span>Number of completed todos:</span>
<CompletedTodosCounter />
</>
)
}