RTK: Есть ли более "изящный" вариант реализации обработки ошибок в extraReducers?
Имеется некий authSlice, в котором я планирую создать extraReducers для 8 actions. Предположим, пока что есть асинхронный action по загрузке пользователя с сервера:
export const loadUser = createAsyncThunk(
"user/loadUser",
async (_, thunkAPI) => {
try {
const response = await AuthService.load_user();
return response.data;
} catch (e) {
return thunkAPI.rejectWithValue("Не удалось загрузить пользователя");
}
}
);
Я создаю для него следующие extraReducers:
export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {},
extraReducers: {
[loadUser.fulfilled.type]: (state, action: PayloadAction<IUser>) => {
state.isLoading = false;
state.error = "";
state.user = action.payload;
state.isAuthenticated = true;
},
[loadUser.pending.type]: (state) => {
state.isLoading = true;
},
[loadUser.rejected.type]: (state, action: PayloadAction<string>) => {
state.isLoading = false;
state.error = action.payload;
},
},
});
[loadUser.fulfilled.type] обрабатывает успешный запрос; [loadUser.rejected.type] обрабатывает запрос в котором произошла ошибка; [loadUser.pending.type] обновляет isLoading из состояния в момент начала отправки запроса.
Я хочу создать 8 различных actions нужных для работы с авторизацией. Для каждого случая прописывать [.rejected.type] и [.pending.type] мне кажется слишком глупо и громоздко, по сути делать они всегда будут одно и то же. Так вот вопрос, можно ли как то обобщить отлов ошибок и состояние "загрузки"?
Ответы (1 шт):
Итак, если кто-то еще столкнется с данной проблемой, я нашел решение.
Для начала я создал 2 стрелочных функции:
const isPendingAction = (action) => action.type.endsWith("/pending");
const isRejectedAction = (action) => action.type.endsWith("/rejected");
Они принимают в себя action и определяют его статус. Далее, в authSlice я добавил следующий код:
export const authSlice = createSlice({
name: "auth",
initialState,
reducers: {},
extraReducers: (builder) => {
builder.addCase(
loadUser.fulfilled,
(state, action: PayloadAction<IUser>) => {
state.isLoading = false;
state.error = "";
state.user = action.payload;
state.isAuthenticated = true;
}
);
builder.addMatcher(
isPendingAction,
(state) => {
state.isLoading = true;
}
);
builder.addMatcher(
isRejectedAction,
(state, action: PayloadAction<string>) => {
state.isLoading = false;
state.error = action.payload;
}
);
},
});
builder.addCase() создает extraReducer для переданного action'а. builder.addMatcher() позволяет осуществлять проверку входящей операции с помощью собственных фильтров.
Получается, что
builder.addMatcher(
isPendingAction,
(state) => {
state.isLoading = true;
}
);
будет отлавливать все actions в authSlice, которые находятся на этапе "загрузки", а
builder.addMatcher(
isRejectedAction,
(state, action: PayloadAction<string>) => {
state.isLoading = false;
state.error = action.payload;
}
);
будет отлавливать все actions в которых произошла ошибка. Теперь для добавления обработчиков новых actions достаточно добавлять один builder.addCase(actionName.fulfilled, (state, action) => {...}), при этом обработка ошибок будет происходить автоматически для всех actions, также как и обработка состояния загрузки.
Вот и ответ на мой вопрос =)