Fetching data with “createAsyncThunk" from RTK(Redux toolkit)

Ever wondered how to fetch async data in RTK. As we all know RTK saves us a lot of time writing immutable code in redux. So let’s fetch async data with createAsyncThunk.

Enough words about redux right? Let’s understand createAsyncThunk.

createAsyncThunk takes three parameters : a string action type value, a payloadCreator callback, and an options object. For example, a type argument of 'users/requestStatus' will generate these action types:

  • pending: 'users/requestStatus/pending'
  • fulfilled: 'users/requestStatus/fulfilled'
  • rejected: 'users/requestStatus/rejected'

payloadCreator:

A callback function that should return a promise containing the result of some asynchronous logic. It may also return a value synchronously. If there is an error, it should either return a rejected promise containing an Error instance or a plain value such as a descriptive error message or otherwise a resolved promise with a RejectWithValue argument as returned by the thunkAPI.rejectWithValue function.

Options

An object with the following optional fields:

  • condition: a callback that can be used to skip execution of the payload creator and all action dispatches, if desired. See Canceling Before Execution for a complete description.
  • dispatchConditionRejection: if condition() returns false, the default behavior is that no actions will be dispatched at all. If you still want a "rejected" action to be dispatched when the thunk was canceled, set this flag to true.

Return Value

createAsyncThunk returns a standard Redux thunk action creator. The thunk action creator function will have plain action creators for the pending, fulfilled, and rejected cases attached as nested fields.

Enough talking right ? Let’s right some code

//postsSlice.js

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

export const getUsers = createAsyncThunk(“posts/getUsers”, () => {

return fetch(“https://jsonplaceholder.typicode.com/posts")

.then((response) => {

if (!response.ok) throw Error(response.statusText);

return response.json();

})

.then((json) => json);

});

export const postsSlice = createSlice({

name: “posts”,

initialState: {

loading: false,

hasErrors: “ “,

posts: [],

},

extraReducers: {

[getUsers.pending]: (state) => {

state.loading = true;

},

[getUsers.rejected]: (state, action) => {

state.loading = false;

state.hasErrors = action.error.message;

},

[getUsers.fulfilled]: (state, { payload }) => {

state.posts = payload;

state.loading = false;

},

},

});

//export const postsSelector = (state) => state.posts;

const appReducer = postsSlice.reducer;

export default appReducer;

Let me break the code for you as I explained above how it takes three parameters so the first string type parameter is our posts/getUsers since I’m fetching posts I just wrote it this way it. getUsers is the function I’m using here and posts happens to be my slice name so it becomes posts/getUsers. It can be written however you want.

createAsyncThunk being a standard redux action creator will have plain action creators for the pending, fulfilled, and rejected cases attached as nested fields. so we have [getUsers.pending], [getUsers.rejected] and [getUsers.fulfilled] . Since it takes a payloadCreator , To get the error message from the reject Promise you’ll access action.error.message.

To get instead the API’s payload you’ll access action.payload.

If you need to access thunk’s parameters to use dispatch or getState, pass the thunkAPI parameter to the callback function (or destructure what you need):

eg) const getUsers = createAsyncThunk("users/getUsers", (thunkAPI) => {...//do something async}

or const getUsers = createAsyncThunk("users/getUsers", (args) => {...//do something async}

I think it’s time not to bore you and show you the code (assummed you knew all about creating & using slices in RTK). Let’s create our store next

//store.js

import { configureStore } from “@reduxjs/toolkit”;

import appReducer from “../features/appSlice”;

const store = configureStore({

reducer: {

posts: appReducer,

},

});

export default store;

//console.log(store.getState())

Next we’ll create Post.js to render our posts.

//Post.js

import React from “react”;

export const Post = ({ post }) => (

<article className=”post-excerpt”>

<h2>{post.title}</h2>

<p>{post.body.substring(0, 100)}</p>

</article>

);

export default Post;

Here comes the App.js file.

//App.js

import React, { useEffect } from “react”;

import { useDispatch, useSelector } from “react-redux”;

import { getUsers, postsSelector } from “./features/appSlice”;

import Post from “./Post”;

function App() {

const dispatch = useDispatch();

//const { posts, loading, error } = useSelector(postsSelector);

const { posts, loading, error } = useSelector(state=>state.posts);

useEffect(() => {

dispatch(getUsers());

}, [dispatch]);

const renderPosts = () => {

if (loading) return <p>Loading posts…</p>;

if (error) return <p>Unable to display posts.</p>;

return posts.map((post) => <Post key={post.id} post={post} excerpt />);

};

return <div>{renderPosts()}</div>;

}

export default App;

And lastly index.js.

I hope you have that created :). so that’s it guys I hope you enjoyed the way I enjoyed writing it.( This happened to be my first medium post).

feel free to ask me if in doubt .

https://twitter.com/carefree_ladka

Happy coding :)