我已经学习redux工具包两个月了,在createSlice
中有reducers
和extrareducers
,我知道它们用于从调度更改状态,但我不知道区别,我们应该在哪里使用它们?
reducers
属性既创建了一个动作创建者函数,又响应切片缩减器中的该动作。extraReducers
允许您响应切片缩减器中的动作,但不创建动作创建者函数。
您将在大多数时间使用reducers
。
当您处理在其他地方已经定义的操作时,您将使用extraReducers
。最常见的例子是响应createAsyncThunk
动作和响应来自另一个切片的动作。
extrareducers
实际上与具有增强功能的reducers
类似,但它的构建是为了处理更多选项,尤其是其他操作(如其他切片中生成的选项或createAction
或createAsyncThunk
执行的操作)。总之
您可以在redux中调度的所有内容都可以添加到其中。
createSlice
中的extrareducers
属性可以用作函数或对象。
函数表单有一个名为builder
的输入参数。
示例1:builder.addCase
函数添加来自另一个切片的操作(具有Typescript类型的安全性)。
const {actions,reducer} = createSlice({
name:'users',
reducers:{
......
},
initialState:.... ,
extraReducers:(builder) => {
builder.addCase(authActions.logout, state => {
state.isLoggedIn = false;
});
}
});
这里的authActions.logout
是来自另一个切片的另一个动作。
如果使用createAsyncThunk
函数(从"@reduxjs/toolkit"导入)创建操作,则可以处理加载、成功和;故障状态。
示例2:处理异步Thunk:的加载状态
const fetchUserById = createAsyncThunk(
'users/fetchByIdStatus',
async (userId, thunkAPI) => {
const response = await userAPI.fetchById(userId)
return response.data
}
)
const {actions,reducer} = createSlice({
name:'users',
reducers:{
......
},
initialState:.... ,
extraReducers: (builder) => {
......
......
builder.addCase(fetchUserById.pending, (state, action) => {
state.loading=true;
state.whoseDataIsLoading = action.payload;
})
}
});
- fetchUserById.pending(处理asyncThunk的加载状态)
- fetchUserById.rejected(处理失败状态)
- fetchUserById已完成(处理成功状态)
构造器还接受addDefaultCase
和addMatcher
,其中addDefaultCase
充当传统减速器(不带工具箱的redux中的减速器)和使用的切换语句中的默认情况
addMatcher与类型谓词函数一起使用,该函数用于推断某些输入具有特定类型(此处为操作类型),以确保操作具有特定类型,然后状态发生变化。
示例3:作为对象的extrareducers
:
const {actions,reducer} = createSlice({
name:'users',
reducers:{
......
},
initialState:.... ,
extraReducers: {
['incrementBy']: (state, action) => {
state.someValue += action.payload
}
}
});
参考文献:
https://redux-toolkit.js.org/api/createSlice#extrareducers
https://redux-toolkit.js.org/usage/usage-with-typescript#type-带减速器的安全性
https://redux-toolkit.js.org/api/createAsyncThunk
https://redux-toolkit.js.org/usage/usage-with-typescript#typing-楼宇地址匹配器
https://redux-toolkit.js.org/api/createReducer#builderadddefaultcase
假设您有这两个减速器:
const booksSlice = createSlice({
name: "books",
initialState: [],
reducers: {
addBook(state, action) {
// this state is not global state. it is only books slice
state.push(action.payload);
},
resetBooks(state, action) {
// immers assume whatever you return you want your state to be
return [];
},
},});
export const { addBook, resetBooks } = booksSlice.actions;
const authorsSlice = createSlice({
name: "authors",
initialState: [],
reducers: {
addAuthor(state, action) {
state.push(action.payload);
},
resetAuthors(state, action) {
return [];
},
},});
export const { addAuthor, resetAuthors } =authorsSlice.actions;
两个减速器的初始状态均为[]。假设您显示";书籍";以及";作者";列表,并且您希望有一个按钮来重置这两种状态。
<button onClick={() => handleResetLists()}>
Clear the page
</button>
在handleResetLists
中,您可以调度两个动作
const handleReset = () => {
dispatch(resetBooks());
dispatch(resetAuthors());
};
但是,我可以告诉authorsSlice
注意使用extraReducers
的其他操作类型
const authorsSlice = createSlice({
name: "authors",
initialState: [],
reducers: {
addAuthor(state, action) {
state.push(action.payload);
},
// builder tells this slice to watch for additional action types
extraReducers(builder) {
// second argument will update the state
builder.addCase(booksSlice.actions.reset, () => {
return [];
});
},
},
});
现在在handleReset
函数中,我将向图书减速器发送一个操作,但作者减速器将关注这个操作,它也将更新
const handleReset = () => {
dispatch(resetBooks());
// I do not need to dispatch this
// dispatch(resetAuthors());
};
在上面的实现中,authorsSlice
依赖于booksSlice
,因为我们在extraReducer
中使用booksSlice.actions.reset
。相反,我们创建了一个单独的操作,因为如果我们在未来的中去掉bookSlice
会怎么样
import {createAction} from "@reduxjs/toolkit"
// we are gonna dispatch this
export const resetAction=createAction("lists/reset")
在两个切片机中添加这个extraReducer:
extraReducers(builder) {
builder.addCase(resetAction, (state, action) => {
state = [];
});
},
页面内
import {resetAction} from "./store/actions"
const handleReset = () => {
dispatch(reset());
// I do not need to dispatch this
// dispatch(resetAction());
};
- 请注意,
extraReducer
不是由slice.actions
生成的操作的一部分
如果操作应该由一个减速器处理,请使用reducers
。
如果操作应该由多个减速器处理,请使用extraReducers
。
假设您有一个TodoReducer.js文件,其中有manageTodo reducer:
import {createSlice} from '@reduxjs/toolkit';
const initialState = {
todo: [],
};
export const manageToDo = createSlice({
name: 'Todo',
initialState: initialState,
reducers: {
add: (state, action) => {
console.log(action);
state.todo = [...state.todo, action.payload];
},
del: (state, action) => {
state.todo.splice(action.payload, 1);
},
edit: (state, action) => {
state.todo.splice(action.payload.index, 1, action.payload.item);
},
clearTodo: state => {
state.todo = [];
},
},
});
你想要的,在加todo时,counter&删除todo时,计数器会递减这样你就可以像这样使用extraReducer
:
CounterReducer.js
import {createSlice} from '@reduxjs/toolkit';
import {manageToDo} from './TodoReducer';
const counterInitialState = {
counter: 0,
};
const manageCounter = createSlice({
name: 'Counter',
initialState: counterInitialState,
reducers: {
increment: (state, action) => {
console.log('testing increment', state.counter);
state.counter++;
},
decrement: (state, action) => {
state.counter--;
},
},
extraReducers: builder => {
builder.addCase(manageToDo.actions.clearTodo, () => {
return 0;
});
builder.addCase(manageToDo.actions.add, state => {
state.counter++;
});
builder.addCase(manageToDo.actions.del, state => {
state.counter--;
});
},
});
export const manageCounterReducer = manageCounter.reducer;
export const {increment, decrement} = manageCounter.actions;
ExtraReducer是reducer,它的作用是做reducer。我在createAsyncThunk()中学习并使用了它。(额外)减速器的作用是具有两个参数的纯函数——实际状态和动作。减速器是减速器的一个切片,而extraReducer是减速器、待处理、已归档和已拒绝的三个切片。每一片(额外的)减速器都是动作的创造者
每个动作创建者都被分派到存储区
Thunk是中间件模式中的函数。Thunk是一个动作创造者,它创造了另外三个动作创造者:未决、完整和拒绝。这三个动作的创造者是三个不变地更新状态的多余的reducer。更多信息:https://www.youtube.com/watch?v=2M-HR2J88S4