英文:
Reducers vs ExtraReducers as a way to handle thunks
问题
I'm currently converting an old redux-codebase to use Redux-Toolkit (RTK), and I can't seem to understand the difference between these approaches. Consider this slice:
interface MyState {
someItems: string[];
}
const myInitialState: MyState = {
someItems: []
};
const mySlice = createSlice({
name: 'mySlice',
initialState: myInitialState,
reducers: {
setItems(state, action: PayloadAction<string[]>) {
state.someItems = action.payload;
}
},
extraReducers: builder => {
builder.addCase(fetchItems.fulfilled, (state, action) => {
state.someItems = action.payload;
});
}
});
My traditional way to use redux has been to call the reducer directly from a Thunk, but I see that the way defined in extraReducers is also possible.
const fetchItems = createAppAsyncThunk(
'mySlice/fetchItems',
async (_, thunkAPI) => {
try {
//Get data from backend
const response = await getItems();
//Option 1
thunkAPI.dispatch(mySlice.actions.setItems(response));
//Option 2
return response;
} catch (e) {
return thunkAPI.rejectWithValue('failed');
}
}
);
What is the difference between these approaches? Are any of them preferred over the other?
Lastly when doing changes I often have to reload the data for latest updates. This time I believe I have to use dispatch
inside the Thunk. Or are there alternatives?
const editItem = createAppAsyncThunk<void, string>(
'mySlice/fetchItems',
async (changedValue, thunkAPI) => {
try {
//Send data to backend
await sendSomethingToBackend(changedValue);
thunkAPI.dispatch(fetchItems());
} catch (e) {
return thunkAPI.rejectWithValue('failed');
}
}
);
英文:
I'm currently converting an old redux-codebase to use Redux-Toolkit (RTK), and I can't seem to understand the difference between these approaches. Consider this slice:
interface MyState {
someItems: string[];
}
const myInitialState: MyState = {
someItems: []
};
const mySlice = createSlice({
name: 'mySlice',
initialState: myInitialState,
reducers: {
setItems(state, action: PayloadAction<string[]>) {
state.someItems = action.payload;
}
},
extraReducers: builder => {
builder.addCase(fetchItems.fulfilled, (state, action) => {
state.someItems = action.payload;
});
}
});
My traditional way to use redux has been to call the reducer directly from a Thunk, but I see that the way defined in extraReducers is also possible.
const fetchItems = createAppAsyncThunk(
'mySlice/fetchItems',
async (_, thunkAPI) => {
try {
//Get data from backend
const response = await getItems();
//Option 1
thunkAPI.dispatch(mySlice.actions.setItems(response));
//Option 2
return response;
} catch (e) {
return thunkAPI.rejectWithValue('failed');
}
}
);
What is the difference between these approaches? Are any of them preferred over the other?
Lastly when doing changes I often have to reload the data for latest updates. This time I believe I have to use dispatch
inside the Thunk. Or are there alternatives?
const editItem = createAppAsyncThunk<void, string>(
'mySlice/fetchItems',
async (changedValue, thunkAPI) => {
try {
//Send data to backend
await sendSomethingToBackend(changedValue);
thunkAPI.dispatch(fetchItems());
} catch (e) {
return thunkAPI.rejectWithValue('failed');
}
}
);
答案1
得分: 1
以下是代码部分的翻译:
给定:
const fetchItems = createAppAsyncThunk(
'mySlice/fetchItems',
async (_, thunkAPI) => {
try {
// 从后端获取数据
const response = await getItems();
// 选项 1
thunkAPI.dispatch(mySlice.actions.setItems(response));
// 选项 2
return response;
} catch (e) {
thunkAPI.rejectWithValue('失败');
}
}
);
这两种方法之间的区别是什么?
在整体方案中,就单个 mySlice
状态片段而言,它们之间没有太大的区别。主要区别在于哪个地方可以处理“resolved”(已解决)的 thunk。
-
分派显式动作:
thunkAPI.dispatch(mySlice.actions.setItems(response));
- 只有
mySlice
的setItems
reducer 函数能够响应并处理此动作。
- 只有
-
分派隐式 resolved(已完成)动作:
return response;
- 任何 Redux-Toolkit(RTK)切片的
extraReducers
函数都可以响应并处理fetchItems.fulfilled
动作。
- 任何 Redux-Toolkit(RTK)切片的
有没有其中一个更受偏爱?
这是主观的,但使用 Thunk 的 .pending
、.fulfilled
和 .rejected
动作可能是普遍被接受的首选方法,因为它提供了更大的灵活性。RTK 的主要目标之一是减少你需要自己编写的代码量。使用自动生成的 Thunk 动作是这一目标的一部分。
const editItem = createAppAsyncThunk<void, string>(
'mySlice/fetchItems',
async (changedValue, thunkAPI) => {
try {
// 将数据发送到后端
await sendSomethingToBackend(changedValue);
thunkAPI.dispatch(fetchItems());
} catch (e) {
thunkAPI.rejectWithValue('失败');
}
}
);
最后,当进行更改时,我经常必须重新加载最新的数据。这次我相信我必须在 thunk 内部使用 dispatch。
是的,在这里,您绝对需要分派 fetchItems
动作,以重新获取sendSomethingToBackend
可能在后端更新的数据。
还有其他选择吗?
如果您正在获取和更新数据,那么对于您来说,一个替代方法可能是使用 Redux-Toolkit Query(RTK Query)。您可以创建一个管理查询和突变的 API 切片。将 fetchItems
Thunk 转换为查询,将 editItem
Thunk 转换为突变,并使用 缓存标签,突变可以使查询数据失效并触发自动重新获取查询。换句话说,进一步减少您编写的代码。
英文:
Given:
> const fetchItems = createAppAsyncThunk(
> 'mySlice/fetchItems',
> async (_, thunkAPI) => {
> try {
> //Get data from backend
> const response = await getItems();
>
> // Option 1
> thunkAPI.dispatch(mySlice.actions.setItems(response));
> // Option 2
> return response;
> } catch (e) {
> thunkAPI.rejectWithValue('failed');
> }
> }
> );
>
> What is the difference between these approaches?
In the grand scheme of things there's not much difference between the two with regards to the single mySlice
state slice. The main difference comes down to what, or where, can handle the "resolved" thunks.
-
Dispatch an explicit action:
thunkAPI.dispatch(mySlice.actions.setItems(response));
- Only the
mySlice
setItems
reducer function can respond and handle this action.
- Only the
-
Dispatch an implicit resolved (fulfilled) action:
return response;
- Any Redux-Toolkit (RTK) slice's
extraReducers
functions can respond and handle thefetchItems.fulfilled
action.
- Any Redux-Toolkit (RTK) slice's
> Are any of them preferred over the other?
This is subjective, but using the Thunk's .pending
, .fulfilled
, and .rejected
actions is likely the generally accepted preferred method as it provides greater flexibility. One of the paramount goals of RTK was to cut down on the amount of code you needed to write yourself. Using the automatically generated Thunk actions is part of this goal.
> const editItem = createAppAsyncThunk<void, string>(
> 'mySlice/fetchItems',
> async (changedValue, thunkAPI) => {
> try {
> //Send data to backend
> await sendSomethingToBackend(changedValue);
>
> thunkAPI.dispatch(fetchItems());
> } catch (e) {
> thunkAPI.rejectWithValue('failed');
> }
> }
> );
>
> Lastly when doing changes I often have to reload the data for latest
> updates. This time I believe I HAVE to use dispatch inside the thunk.
Yes, here you absolutely would need to dispatch the fetchItems
action to refetch data that sendSomethingToBackend
may've updated in the backend.
> Or are there alternatives?
If you are fetching and updating data then an alternative for you may be to use Redux-Toolkit Query (RTK Query). You'd create an API slice that manages queries and mutations. Convert the fetchItems
Thunk to a query, and the editItem
Thunk to a mutation, and using cache tags the mutations can invalidate queried data and trigger queries to re-fetch automatically. In other words, further cutting down of code you write.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论