Async Operations in Redux with the Redux Toolkit Thunk

Async Operations in Redux with the Redux Toolkit Thunk

·

3 min read

Table of contents

In this writing, I have compiled my work on Thunk. Happy reading!

Content:

What is the redux-toolkit thunk?

Introduction

Let’s try to see the big picture by initially making brief explanations about “redux-toolkit” and “thunk”.

Redux-toolkit: You can think of it as an extension of the Redux library and is used to simplify data management in an application. Redux-toolkit supports async operations called “thunk”.

Thunk: It is a function that delays the execution of a function or block of code until it is called again. In Redux, it is used to manage async operations (e.g. fetching data or database operations). With this extension, managing async operations become easier and your application can become more flexible.

Conclusion

The Redux Toolkit Thunk is a middleware for the Redux library that allows you to write async logic that interacts with the store. It is designed to make it easier to work with asynchronous actions in Redux, and it enables you to write action creators that return a function instead of an action object. This function called a “thunk” can be used to perform async logic, such as making an API request or dispatching multiple actions. The thunk can also be used to perform conditional logic, such as dispatching a different action depending on the result of the async operation. Overall, the Redux Toolkit Thunk is a powerful tool for managing async behavior in a Redux application.

Practical Example Project

// index.js

import React from 'react'
import ReactDOM from 'react-dom/client'
import './index.css'
import App from './App'

import { store } from './redux/config/store'
import { Provider } from 'react-redux'

const root = ReactDOM.createRoot(document.getElementById('root'))
root.render(
  <React.StrictMode>
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>
)
// store.js

import { configureStore } from '@reduxjs/toolkit'
import contentSlice from '../slice/contentSlice'

export const store = configureStore({
  reducer: {
    content: contentSlice,
  },
})
// webpack.config.jsx
const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin')
const BundleAnalyzerPlugin =
  require('webpack-bundle-analyzer').BundleAnalyzerPlugin

module.exports = {
  mode: 'production',
  entry: path.resolve(__dirname, 'src/index.js'),
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: 'index.js',
    clean: true,
  },
  //devtool: 'source-map',
  devServer: {
    static: {
      directory: path.resolve(__dirname, 'dist'),
    },
    port: 3000,
    open: true,
    hot: true,
    compress: true,
    historyApiFallback: true,
  },
  module: {
    rules: [
      {
        test: /\.s[ac]ss$/i,
        use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'],
      },
      {
        test: /\.html$/,
        use: [
          {
            loader: 'html-loader',
          },
        ],
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
        },
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
    ],
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: path.resolve(__dirname, 'public', 'index.html'),
    }),
    //new BundleAnalyzerPlugin(),
  ],
  resolve: {
    extensions: ['.js', '.jsx', '.md'],
    modules: [path.resolve(__dirname, 'src'), 'node_modules'],
  },
// contentSlice.js

import { createAsyncThunk, createSlice } from '@reduxjs/toolkit'
import axios from 'axios'

const initialState = {
  contents: [],
  isLoading: false,
  error: null,
}

export const fetchContent = createAsyncThunk(
  'content/fetchContent',
  async () => {
    const res = await axios('https://jsonplaceholder.typicode.com/photos')
    const data = await res.data
    return data
  }
)

export const contentSlice = createSlice({
  name: 'content',
  initialState,
  reducers: {},
  extraReducers: (builder) => {
    builder.addCase(fetchContent.pending, (state) => {
      state.isLoading = true
    })
    builder.addCase(fetchContent.fulfilled, (state, action) => {
      state.isLoading = false
      state.contents = action.payload
    })
    builder.addCase(fetchContent.rejected, (state, action) => {
      state.isLoading = false
      state.error = action.error.message
    })
  },
})

export default contentSlice.reducer
// App.js

function App() {
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(fetchContent())
  }, [dispatch])

  const contents = useSelector((state) => state.content.contents)
  const isLoading = useSelector((state) => state.content.isLoading)
  const error = useSelector((state) => state.content.error)

  if (isLoading) {
    return 'loading...'
  }

  if (error) {
    return error
  }

  return (
    <div className='grid gap-4 grid-cols-2  md:grid-cols-4 lg:grid-cols-8  p-4'>
      {contents.map((content) => (
        <div key={content.id}>
          <img
            src={`${content.thumbnailUrl}`}
            alt={`${content.title}`}
            className='w-full h-full rounded'
          />
        </div>
      ))}
    </div>
  )
}

export default App

🎒 Repository --> ozantekin/redux-thunk-basic

🏄🏻‍♂️ Video

🔗 Learn more about me here.