How to use Redux-Thunk with Redux Toolkit's createSlice?

I'm a Redux maintainer and creator of Redux Toolkit.

FWIW, nothing about making async calls with Redux changes with Redux Toolkit.

You'd still use an async middleware (typically redux-thunk), fetch data, and dispatch actions with the results.

As of Redux Toolkit 1.3, we do have a helper method called createAsyncThunk that generates the action creators and does request lifecycle action dispatching for you, but it's still the same standard process.

This sample code from the docs sums up the usage;

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

// First, create the thunk
const fetchUserById = createAsyncThunk(
  'users/fetchByIdStatus',
  async (userId, thunkAPI) => {
    const response = await userAPI.fetchById(userId)
    return response.data
  }
)

// Then, handle actions in your reducers:
const usersSlice = createSlice({
  name: 'users',
  initialState: { entities: [], loading: 'idle' },
  reducers: {
    // standard reducer logic, with auto-generated action types per reducer
  },
  extraReducers: {
    // Add reducers for additional action types here, and handle loading state as needed
    [fetchUserById.fulfilled]: (state, action) => {
      // Add user to the state array
      state.entities.push(action.payload)
    }
  }
})

// Later, dispatch the thunk as needed in the app
dispatch(fetchUserById(123))

See the Redux Toolkit "Usage Guide: Async Logic and Data Fetching" docs page for some additional info on this topic.

Hopefully that points you in the right direction!


Use redux-toolkit v1.3.0-alpha.8

Try this

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

export const myAsyncInSlice = createAsyncThunk('bundles/myAsyncInSlice', () =>
  getAxiosInstance()
    .get('/')
    .then(ok => ok.data)
    .catch(err => err),
);

const usersSlice = createSlice({
  name: 'bundles',
  initialState: {
    bundles: [],
    selectedBundle: null,
    page: {
      page: 0,
      totalElements: 0,
      size: 20,
      totalPages: 0,
    },
    myAsyncResponse: null,
    myAsyncResponseError: null,
  },
  reducers: {
    // add your non-async reducers here
  },
  extraReducers: {
    // you can mutate state directly, since it is using immer behind the scenes
    [myAsyncInSlice.fulfilled]: (state, action) => {
      state.myAsyncResponse = action.payload;
    },
    [myAsyncInSlice.rejected]: (state, action) => {
      state.myAsyncResponseError = action.payload;
    },
  },
});



You can use createAsyncThunk to create thunk action, which can be trigger using dispatch

teamSlice.ts

import {
  createSlice,
  createAsyncThunk,
} from "@reduxjs/toolkit";
const axios = require('axios');

export const fetchPlayerList = createAsyncThunk('team/playerListLoading', 
  (teamId:string) =>
  axios
    .get(`https://api.opendota.com/api/teams/${teamId}/players`)
    .then(response => response.data)
    .catch(error => error),
);

const teamInitialState = {
   playerList: {
     status: 'idle',
     data: {},
     error: {}
   }    
};

const teamSlice = createSlice({
  name: 'user',
  initialState: teamInitialState,
  reducers: {},
  extraReducers: {
    [fetchPlayerList.pending.type]: (state, action) => {
        state.playerList = {
        status: 'loading',
        data: {},
        error: {}
      };
    },
    [fetchPlayerList.fulfilled.type]: (state, action) => {
        state.playerList = {
        status: 'idle',
        data: action.payload,
        error: {}
     };
    },
    [fetchPlayerList.rejected.type]: (state, action) => {
        state.playerList = {
        status: 'idle',
        data: {},
        error: action.payload,
      };
    },
  }
});

export default teamSlice;

Team.tsx component

import React from "react";
import { useSelector, useDispatch } from "react-redux";

import { fetchPlayerList } from './teamSlice';

const Team = (props) => {
  const dispatch = useDispatch();
  const playerList = useSelector((state: any) => state.team.playerList);

  return (
    <div>
      <button
        onClick={() => { dispatch(fetchPlayerList('1838315')); }}
      >Fetch Team players</button>

      <p>API status {playerList.status}</p>
      <div>
        { (playerList.status !== 'loading' && playerList.data.length) &&
          playerList.data.map((player) => 
            <div style={{display: 'flex'}}>
              <p>Name: {player.name}</p>
              <p>Games Played: {player.games_played}</p>
            </div>
          )
        }
      </div>
    </div>
  )
}

export default Team;