使用额外的 reducer 与在异步 thunk 中调度的优缺点
Pros & cons of using extra reducers vs dispatching within async thunk
我对整个 React 和 Redux 生态系统还比较陌生,我正在尝试了解何时以及为何在使用 Redux 工具包时使用额外的 reducer 与直接在异步 thunk 中调度操作。
可能最好用一个显示两种解决方案的例子来解释:
版本 1:使用额外的减速器
auth.slice.ts
// ...
export const login = createAsyncThunk<LoginResponse, LoginData>(
'auth/login',
async ({ email, password }, thunkAPI) => {
const data = await AuthService.login(email, password);
// Extract user info from login response which holds other information as well
// in which we're not interested in the auth slice...
const userInfo = loginResponseToUserInfo(data);
LocalStorageService.storeUserInfo(userInfo);
// Return the whole login response as we're interested in the other data
// besides the user info in other slices which handle `login.fulfilled` in
// their own `extraReducers`
return data;
}
);
// ...
const authSlice = createSlice({
// ...
extraReducers: builder => {
builder.addCase(login.fulfilled, (state, { payload }) => {
// Again: Extract user info from login response which holds other
// information as well in which we're not interested in the auth slice...
const userInfo = loginResponseToUserInfo(payload);
return { ...state, userInfo };
}))
// ...
},
});
// ...
版本 2:在异步 thunk 中使用分派
auth.slice.ts
// ...
export const login = createAsyncThunk<LoginResponse, LoginData>(
'auth/login',
async ({ email, password }, thunkAPI) => {
const data = await AuthService.login(email, password);
// Extract user info from login response which holds other information as well
// in which we're not interested in the auth slice...
const userInfo = loginResponseToUserInfo(data);
LocalStorageService.storeUserInfo(userInfo);
// !!! Difference to version 1 !!!
// Directly dispatch the action instead of using `extraReducer` to further
// process the extracted user info
thunkAPI.dispatch(authSlice.actions.setUserInfo(userInfo));
// Return the whole login response as we're interested in the other data
// besides the user info in other slices which handle `login.fulfilled` in
// their own `extraReducers`
return data;
}
);
// ...
const authSlice = createSlice({
// ...
reducers: {
setUserInfo: (state, { payload }: PayloadAction<UserInfo>) => ({
...state,
userInfo: payload,
}),
// ...
},
});
// ...
问题
如果我没有完全错的话,这两个例子做的事情完全一样,但是通过互联网我发现大多数人建议使用 extraReducer
的选项 1,这就是我问的原因:
- 这两个版本基本上都是 ok/correct 还是我遗漏了什么?
- 坚持“
extraReducers
方法”有什么好处吗?
这个特定示例中的一个小缺点是我必须在 2 个地方(异步 thunk 和 extraReducer
)使用 loginResponseToUserInfo
执行转换,而我只需要在第二个版本中调用它一次。 .
在我看来两者都是有效的,尽管我个人会选择#1。
证明我的选择:
- 你应该把'actions'当成'events',事件是'user logon successfull'。将数据设置到切片中的明确操作有点错误模式
- 将您的每个切片视为应该能够独立工作的子模块。负责认证的模块不应该关心其他切片是否正在监听它的事件;将来您可能在此事件中有其他切片,并且您不希望在您的 thunk 中以几个无关的分派结束 + 'logon success' 事件可能会从另一个来源触发。
我对整个 React 和 Redux 生态系统还比较陌生,我正在尝试了解何时以及为何在使用 Redux 工具包时使用额外的 reducer 与直接在异步 thunk 中调度操作。
可能最好用一个显示两种解决方案的例子来解释:
版本 1:使用额外的减速器
auth.slice.ts
// ...
export const login = createAsyncThunk<LoginResponse, LoginData>(
'auth/login',
async ({ email, password }, thunkAPI) => {
const data = await AuthService.login(email, password);
// Extract user info from login response which holds other information as well
// in which we're not interested in the auth slice...
const userInfo = loginResponseToUserInfo(data);
LocalStorageService.storeUserInfo(userInfo);
// Return the whole login response as we're interested in the other data
// besides the user info in other slices which handle `login.fulfilled` in
// their own `extraReducers`
return data;
}
);
// ...
const authSlice = createSlice({
// ...
extraReducers: builder => {
builder.addCase(login.fulfilled, (state, { payload }) => {
// Again: Extract user info from login response which holds other
// information as well in which we're not interested in the auth slice...
const userInfo = loginResponseToUserInfo(payload);
return { ...state, userInfo };
}))
// ...
},
});
// ...
版本 2:在异步 thunk 中使用分派
auth.slice.ts
// ...
export const login = createAsyncThunk<LoginResponse, LoginData>(
'auth/login',
async ({ email, password }, thunkAPI) => {
const data = await AuthService.login(email, password);
// Extract user info from login response which holds other information as well
// in which we're not interested in the auth slice...
const userInfo = loginResponseToUserInfo(data);
LocalStorageService.storeUserInfo(userInfo);
// !!! Difference to version 1 !!!
// Directly dispatch the action instead of using `extraReducer` to further
// process the extracted user info
thunkAPI.dispatch(authSlice.actions.setUserInfo(userInfo));
// Return the whole login response as we're interested in the other data
// besides the user info in other slices which handle `login.fulfilled` in
// their own `extraReducers`
return data;
}
);
// ...
const authSlice = createSlice({
// ...
reducers: {
setUserInfo: (state, { payload }: PayloadAction<UserInfo>) => ({
...state,
userInfo: payload,
}),
// ...
},
});
// ...
问题
如果我没有完全错的话,这两个例子做的事情完全一样,但是通过互联网我发现大多数人建议使用 extraReducer
的选项 1,这就是我问的原因:
- 这两个版本基本上都是 ok/correct 还是我遗漏了什么?
- 坚持“
extraReducers
方法”有什么好处吗?
这个特定示例中的一个小缺点是我必须在 2 个地方(异步 thunk 和extraReducer
)使用loginResponseToUserInfo
执行转换,而我只需要在第二个版本中调用它一次。 .
在我看来两者都是有效的,尽管我个人会选择#1。
证明我的选择:
- 你应该把'actions'当成'events',事件是'user logon successfull'。将数据设置到切片中的明确操作有点错误模式
- 将您的每个切片视为应该能够独立工作的子模块。负责认证的模块不应该关心其他切片是否正在监听它的事件;将来您可能在此事件中有其他切片,并且您不希望在您的 thunk 中以几个无关的分派结束 + 'logon success' 事件可能会从另一个来源触发。