Skip to main content

How is a thunk different from a regular action?

Short answer

A regular action is an object that describes what happened (for example: { type: 'INCREMENT', payload: 1 }).

A thunk is a function that describes what needs to be done (for example: dispatch -> API -> dispatch SUCCESS/ERROR).


1. Regular action

Redux is synchronous by nature. Every action is a plain object with a required type field:

javascript
const increment = { type: 'INCREMENT', payload: 1 }

You pass it to the store:

javascript
store.dispatch(increment)

-> Redux immediately calls the reducer -> the reducer synchronously returns a new state.

Everything is simple and instant.


2. Thunk (an asynchronous action)

A thunk is a function, not an object. When Redux sees a function, it doesn't know by itself what to do with it - so redux-thunk middleware comes into play.

javascript
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 }) } }

Here the thunk is an "action process" that internally:

  1. runs asynchronous code (an API request),
  2. and only then dispatches regular (synchronous) actions.

3. Comparison in code

TypeWhat it isWhat it looks likeWhat it does
Regular actionObject{ type: 'ADD_TODO', payload: 'Buy milk' }Goes straight to the reducer
ThunkFunction(dispatch, getState) => { ... }Runs logic (usually asynchronous), then dispatches regular actions

4. Example in action

Regular action:

javascript
dispatch({ type: 'LOGIN_SUCCESS', payload: user })

Updates the store right away.


Thunk action:

javascript
dispatch(loginUser(credentials))
javascript
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 }) } }

Here:

  1. we start loading (LOGIN_START),
  2. we wait for the server response,
  3. we finish with a success or error action.

5. What happens inside Redux

Without a thunk:

javascript
dispatch({ type: 'INCREMENT' }) reducer(state, action) new state

With a thunk:

javascript
dispatch(fetchUser(5)) redux-thunk sees that this is a function calls it -> fetch API -> dispatch regular actions reducer processes them new state

6. The main difference

CriterionRegular ActionThunk
TypeObjectFunction
AsynchronousNoYes
Where it goesStraight to the reducerTo middleware (redux-thunk)
Can dispatch other actionsNoYes
Has access to dispatch / getStateNoYes
Used forSimple changesComplex logic (requests, chains, timers)

7. Visually

javascript
Regular action: [dispatch][reducer][new state] Thunk: [dispatch][redux-thunk middleware] runs the function [dispatch regular actions][reducer]

8. Example in Redux Toolkit

Redux Toolkit includes redux-thunk by default, and its counterpart is createAsyncThunk(), which automates working with thunks.

javascript
export const fetchUser = createAsyncThunk('user/fetch', async (id) => { const res = await fetch(`/api/users/${id}`) return await res.json() })

Under the hood, createAsyncThunk is simply an automatically created thunk with pending, fulfilled, rejected actions.


Summary

DifferenceRegular ActionThunk
TypeObject { type, payload }Function (dispatch, getState) => {}
Handled byStraight to the reducerredux-thunk middleware
AsynchronousNoYes
PurposeDescribe "what happened"Describe "what needs to be done"
Can dispatch other actionsNoYes
Used forSimple eventsRequests, complex logic

In short:

A regular action is a message about an event. A thunk is a function-plan of actions that can do asynchronous work and dispatch the necessary regular actions itself at the right moment.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.