DEV Community


Posted on

React Data Fetching Pattern - Part III

This article is a continuation of

Click here to access the Github repo for the code examples discussed below.

Redux createAsyncThunk

While React Context API is suitable for smaller apps, we need a more performant solution for larger apps.

Redux provides a solution to the problem of unnecessary re-rendering of components posed by Context API, as Redux only re-renders the updated component. Within the Redux-toolkit library, one has access to an API known as createAsyncThunk.

createAsyncThunk will accept a callback function that returns a promise - this is where the getUsersAPI function will reside. Upon execution of the createAsyncThunk function, similar to a Promise function, it will generate 3 types of actions: pending, fulfilled, rejected, and the users state will be updated accordingly depending on the action type.
Below is an example of the code:

interface UsersDataState {
  users: User[]
  loading: boolean
  error: string

const initialState: UsersDataState = {
  users: [] as User[],
  loading: false,
  error: "",

// set up createAsyncThunk function
export const getUsersData = createAsyncThunk("users/fetchUsers", () => {
  return getUsers(apiURL).then(({results}) => results)
Enter fullscreen mode Exit fullscreen mode
const userSlice = createSlice({
  name: "user",
  reducers: {},

  extraReducers: (builder) => {
// Pending - users state is not updated
    builder.addCase(getUsersData.pending, (state) => {
      state.loading = true

// Fulfilled - users state is updated
    builder.addCase(getUsersData.fulfilled, (state, action: PayloadAction<UserApi[]>) => {
      state.users = action.payload
        .map((user: UserApi) => {
          return {
            username: user.login.username,

      state.loading = false

// Rejected - users state is an empty array
    builder.addCase(getUsersData.rejected, (state, action) => {
      state.loading = false
      state.users = []
      state.error = action.error.message || "Something went wrong"
Enter fullscreen mode Exit fullscreen mode

For a React component to have access to the users state using Redux, one also has to set up a redux store and then wrap the <App /> component with Redux Provider. You may refer to the boilerplate code in the repo here for further details.

Having done the above, let's see how we can retrieve the data in the component for UI rendering.

As shown below, within a React component, one has access to 2 redux hooks:

  • useAppSelector - enable access to states
  • useAppDispatch - enable update to states

We can use useEffect hook to trigger the data fetching and updating of the users state. Then the state will be used for UI rendering.

export default function CardList() {
    const {users, loading, error} = useAppSelector((state) => state.user)
    const dispatch = useAppDispatch()

    // update of users state with useEffect
    useEffect(() => {

  return (
    <div className="grid sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 justify-center grid-cols-1 mt-8">
        ? <Loading />
        : error 
        ? <ErrorFallBack />
        : => (<CardItem key={} user={user}/>))
Enter fullscreen mode Exit fullscreen mode


  • More Performant than Context API: As Redux avoids unnecessary re-rendering, the app is more performant as a result.


  • Complicated setup: As demonstrated above, there are a lot of boilerplate codes to set up redux slicer and store.

Having discussed a few data fetching methods, could there be a solution that is both simple to set up and performant? Let's proceed to look at the next method - SWR.

To be continued:

For more information on Redux createAsyncThunk, see here.

If you like the content, please hit the like button so that it can reach more people.

Top comments (0)