Чим thunk відрізняється від звичайного action?
Коротка відповідь
Звичайний action - це об'єкт, який описує що сталося (наприклад:
{ type: 'INCREMENT', payload: 1 }).Thunk - це функція, яка описує що потрібно зробити (наприклад:
dispatch -> API -> dispatch SUCCESS/ERROR).
1. Звичайний action
Redux за своєю природою синхронний.
Кожна дія - це plain object (простий об'єкт) з обов'язковим полем type:
const increment = {
type: 'INCREMENT',
payload: 1
}Ти передаєш його в store:
store.dispatch(increment)-> Redux одразу викликає reducer -> reducer синхронно повертає новий стан.
Все просто і миттєво.
2. Thunk (асинхронний action)
Thunk - це функція, а не об'єкт. Коли Redux бачить функцію, він сам по собі не знає, що з нею робити - тому в справу вступає middleware redux-thunk.
const fetchUser = (id) => {
return async (dispatch, getState) => {
dispatch({ type: 'USER_FETCH_START' })
const response = await fetch(`/api/users/${id}`)
const data = await response.json()
dispatch({ type: 'USER_FETCH_SUCCESS', payload: data })
}
}Тут thunk - це "дія-процес", яка всередині себе:
- виконує асинхронний код (API-запит),
- і вже потім робить
dispatchзвичайних (синхронних) actions.
3. Порівняння в коді
| Тип | Що це | Як виглядає | Що робить |
|---|---|---|---|
| Звичайний action | Об'єкт | { type: 'ADD_TODO', payload: 'Buy milk' } | Одразу йде в reducer |
| Thunk | Функція | (dispatch, getState) => { ... } | Виконує логіку (зазвичай асинхронну), потім dispatch'ить звичайні actions |
4. Приклад у дії
Звичайний action:
dispatch({ type: 'LOGIN_SUCCESS', payload: user })Одразу оновить store.
Thunk action:
dispatch(loginUser(credentials))const loginUser = (credentials) => async (dispatch) => {
dispatch({ type: 'LOGIN_START' })
try {
const res = await fetch('/api/login', { method: 'POST', body: JSON.stringify(credentials) })
const user = await res.json()
dispatch({ type: 'LOGIN_SUCCESS', payload: user })
} catch (err) {
dispatch({ type: 'LOGIN_ERROR', payload: err.message })
}
}Тут:
- починаємо завантаження (
LOGIN_START), - чекаємо відповіді від сервера,
- завершуємо дією успіху або помилки.
5. Що відбувається всередині Redux
Без thunk:
dispatch({ type: 'INCREMENT' })
↓
reducer(state, action)
↓
новий станЗ thunk:
dispatch(fetchUser(5))
↓
redux-thunk бачить, що це функція
↓
викликає її -> fetch API -> dispatch звичайних actions
↓
reducer обробляє їх
↓
новий стан6. Головна різниця
| Критерій | Звичайний Action | Thunk |
|---|---|---|
| Тип | Об'єкт | Функція |
| Асинхронність | Ні | Так |
| Куди йде | Одразу в reducer | В middleware (redux-thunk) |
| Може викликати інші actions | Ні | Так |
Має доступ до dispatch / getState | Ні | Так |
| Використовується для | Простих змін | Складної логіки (запити, ланцюжки, таймери) |
7. Візуально
Звичайний action:
[dispatch] → [reducer] → [новий state]
Thunk:
[dispatch] → [redux-thunk middleware] →
виконує функцію →
[dispatch звичайних actions] → [reducer]8. Приклад у Redux Toolkit
Redux Toolkit включає redux-thunk за замовчуванням,
а його аналог - це createAsyncThunk(), який автоматизує роботу з thunks.
export const fetchUser = createAsyncThunk('user/fetch', async (id) => {
const res = await fetch(`/api/users/${id}`)
return await res.json()
})Під капотом createAsyncThunk - це просто автоматично створений thunk
з екшенами pending, fulfilled, rejected.
Підсумок
| Відмінність | Звичайний Action | Thunk |
|---|---|---|
| Тип | Об'єкт { type, payload } | Функція (dispatch, getState) => {} |
| Обробляється | Одразу в reducer | Middleware redux-thunk |
| Асинхронність | Немає | Є |
| Мета | Описати "що сталося" | Описати "що потрібно зробити" |
| Може dispatch'ити інші actions | Ні | Так |
| Використовується для | Простих подій | Запитів, складної логіки |
Коротко:
Звичайний action - це повідомлення про подію. Thunk - це функція-план дій, яка може виконувати асинхронну роботу і сама відправляти потрібні звичайні actions у потрібний момент.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.