Редюсери та екстраредюсери в 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;
    }
}

Ключові принципи редюсера:

  1. Чистота функції. Редюсер не має побічних ефектів.
  2. Незмінність стану. Стан не змінюється напряму, а створюється новий об’єкт із внесеними змінами.
  3. Детермінізм. Вхідні дані завжди дають однаковий результат.

Що таке екстраредюсер?

Екстраредюсер (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
Основне використання Простий стан і синхронні дії Асинхронні дії чи складна логіка

Коли використовувати екстраредюсери?

  1. Асинхронні операції. Якщо ваш додаток виконує асинхронні запити (наприклад, до API), екстраредюсери ідеально підходять для обробки різних станів (loading, succeeded, failed).
  2. Спільні дії. Коли кілька слайсів мають реагувати на одну й ту саму дію.
  3. Глобальні дії. Обробка подій, які не пов’язані з конкретним слайсом.

Як створити редюсер з екстраредюсерами

Крок 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. Редюсери обробляють синхронні та локальні дії, тоді як екстраредюсери дозволяють розширювати логіку, обробляючи асинхронні або глобальні дії. Використовуючи ці підходи, можна створювати масштабовані та добре організовані додатки.