Редюсери та екстраредюсери в Redux: Як вони працюють і в чому різниця
Redux — це популярна бібліотека для управління станом у JavaScript-додатках, особливо в React. У центрі роботи Redux стоїть концепція редюсерів. Сьогодні ми розглянемо, що таке редюсери та екстраредюсери, як вони працюють і чим відрізняються, а також подивимося приклади використання.
Що таке редюсер?
Редюсер (reducer) — це функція, яка визначає, як змінюється стан додатка у відповідь на дії (actions). Редюсери є чистими функціями: вони приймають поточний стан та дію і повертають новий стан.
Приклад редюсера:
const initialState = { count: 0 };
function counterReducer(state = initialState, action) {
    switch (action.type) {
        case 'INCREMENT':
            return { ...state, count: state.count + 1 };
        case 'DECREMENT':
            return { ...state, count: state.count - 1 };
        default:
            return state;
    }
}
Ключові принципи редюсера:
- Чистота функції. Редюсер не має побічних ефектів.
 - Незмінність стану. Стан не змінюється напряму, а створюється новий об’єкт із внесеними змінами.
 - Детермінізм. Вхідні дані завжди дають однаковий результат.
 
Що таке екстраредюсер?
Екстраредюсер (extra reducer) — це концепція, яка використовується в Redux Toolkit, сучасному наборі інструментів для роботи з Redux. Екстраредюсери дозволяють обробляти дії, які визначені поза межами конкретного слайсу (slice).
Основна ідея екстраредюсерів
Звичайні редюсери пов’язані з діями, що створюються у межах одного слайсу. Екстраредюсери дозволяють обробляти:
- дії з інших слайсів,
 - глобальні дії,
 - асинхронні дії, створені за допомогою 
createAsyncThunk. 
Приклад екстраредюсерів:
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
// Асинхронна дія
export const fetchUser = createAsyncThunk('user/fetchUser', async (userId) => {
    const response = await fetch(`/api/users/${userId}`);
    return response.json();
});
const userSlice = createSlice({
    name: 'user',
    initialState: { user: null, status: 'idle' },
    reducers: {},
    extraReducers: (builder) => {
        builder
            .addCase(fetchUser.pending, (state) => {
                state.status = 'loading';
            })
            .addCase(fetchUser.fulfilled, (state, action) => {
                state.status = 'succeeded';
                state.user = action.payload;
            })
            .addCase(fetchUser.rejected, (state) => {
                state.status = 'failed';
            });
    },
});
export default userSlice.reducer;
У цьому прикладі екстраредюсери обробляють асинхронні дії, створені через createAsyncThunk.
В чому різниця між редюсерами та екстраредюсерами?
| Характеристика | Редюсер | Екстраредюсер | 
|---|---|---|
| Зв’язок із діями | Обробляє дії, визначені в тому ж слайсі | Обробляє дії з інших слайсів чи глобальні | 
| Декларативність | Визначається через об’єкт у reducers | 
Визначається через метод builder | 
| Основне використання | Простий стан і синхронні дії | Асинхронні дії чи складна логіка | 
Коли використовувати екстраредюсери?
- Асинхронні операції. Якщо ваш додаток виконує асинхронні запити (наприклад, до API), екстраредюсери ідеально підходять для обробки різних станів (loading, succeeded, failed).
 - Спільні дії. Коли кілька слайсів мають реагувати на одну й ту саму дію.
 - Глобальні дії. Обробка подій, які не пов’язані з конкретним слайсом.
 
Як створити редюсер з екстраредюсерами
Крок 1: Створіть асинхронну дію (за потреби):
export const fetchPosts = createAsyncThunk('posts/fetchPosts', async () => {
    const response = await fetch('/api/posts');
    return response.json();
});
Крок 2: Створіть слайс із екстраредюсерами:
const postsSlice = createSlice({
    name: 'posts',
    initialState: { items: [], status: 'idle' },
    reducers: {},
    extraReducers: (builder) => {
        builder
            .addCase(fetchPosts.pending, (state) => {
                state.status = 'loading';
            })
            .addCase(fetchPosts.fulfilled, (state, action) => {
                state.status = 'succeeded';
                state.items = action.payload;
            })
            .addCase(fetchPosts.rejected, (state) => {
                state.status = 'failed';
            });
    },
});
export default postsSlice.reducer;
Висновок
Редюсери та екстраредюсери — це ключові інструменти для управління станом у Redux. Редюсери обробляють синхронні та локальні дії, тоді як екстраредюсери дозволяють розширювати логіку, обробляючи асинхронні або глобальні дії. Використовуючи ці підходи, можна створювати масштабовані та добре організовані додатки.