Data fetching

Technology Review
Written by: Federico Castañares, Facundo Panizza

Modern Redux, RTK, Redux Query

Pros:

  • Avoids a lot of unnecessary boilerplate.
  • Has a fairly easy-to-use cache management, as well as cache invalidation.
  • Supports error handling and loading states.

Cons:

  • Not necessary, but it is recommended to know the conventional way of Redux for a better understanding of how it works.

Example:

Query

import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react';

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({ baseUrl: '/api' }),
  tagTypes: [Users],
  endpoints: (builder) => ({
    getUsers: builder.query({
      query: () => 'users',
      providesTags: ['Users'],
    }),
    createUser: build.mutation({
      query: (body) => ({
        url: 'users',
        method: 'POST',
        body,
      }),
      invalidatesTags: ['Users'],
    })
  }),
});

export const { useGetUsersQuery } = api;


This will create a cache for the query “getUsers” and when a new user is created is going to refresh that cache.

import { useGetUsersQuery } from './api';

const UsersList = () => {
  const { data, isLoading, isError } = useGetUsersQuery();

  if (isLoading) {
    return <p>Loading...</p>;
  }

  if (isError) {
    return <p>Error fetching data</p>;
  }

  return (
    <div>
      <h1>Users List</h1>
      <ul>
        {data.map((user) => (
          <li key={user.id}>{user.name}</li>
        ))}
      </ul>
    </div>
  );
};

export default UsersList;

Conventional Redux

Create async thunk

The idea of Redux is to centralize the state of our application in our store and separate our state from our components, providing persistence in the data.

As mentioned earlier, the implementation of Modern Redux requires less code and comes with built-in methods that are often necessary to write if we don’t have them.

Nevertheless, let’s take a look at the flow.

Pros:

  • Separation of concerns; on one hand, we have the actions, on the other hand, the services, and finally, how the combination of these two changes the state of our application.

Cons:

  • Requires writing a considerable amount of boilerplate.
  • Initially, communication between each responsibility may seem confusing.

The Flow

Dispatch in our components to an action: dispatch(createCashierAction(CashierLoad));

Action

We create our action that will call the service, then from our slice, we will listen to the response whether it fails or not. If it fails, we have the ability to send the error message to the slice. In the example below, we return the error message with the error response structure of a service with NestJS. We can use this or use our own messages or use just one generic.