Using RTK Query with Redux Toolkit [На примере Next.js + TypeScript]


Introduction

Hello everyone 🙂 I would like to talk about using RTK Query + Redux Toolkit in large projects, as well as speed up your development and adding new pages by getting rid of unnecessary code duplication.

If you are a react frontend developer, then I think this is not the first time you have come across the use of RTK Query along with the Redux Toolkit. But even if this is so, then I think I have something new to show you;) And if you have never encountered this bunch, then perhaps after this article you will think about using them.

Foreword

Recommended download project and while reading, look into the code, because some points are easier to see and understand than to read dozens of words and sentences.

Installing dependencies: yarn install. Command run: yarn dev.

The main problems of this bundle of libraries

  • It is necessary to constantly write loaders that would show the page loading, and then draw the received data.

  • Sometimes you want to get just the data from the request without creating a separate slice for the page, but sometimes the other way around.

  • It is always necessary to think about handling unexpected errors when adding new pages to the service and draw an error window. At the same time, I want to avoid using a global handler.

  • If 2 or more requests are needed, then you have to write the logic separately and combine the requests. And if one of them fails, write the logic of a repeated request.

  • When adding data to a slice in the onQueryStarted method, the data simply does not have time to get into the storage before the component that uses it is rendered.

  • Requesting some kind of mutation can invalidate the tag, causing the entire page to be reloaded, even when it’s not necessary.

I hasten to please you, I solved these problems and moreover with a minimum amount of code, so let’s move on to the solution and the project itself.

Project Structure

We put all the main logic in the redux folder. We separate api requests and slices into separate folders. For a better understanding, think of api as the top layer above slices. Api works, performs some of its manipulations and puts them in storage. The repository doesn’t need to know who puts what into it, it just needs to know what to do with the data. An api can access slices, but slices cannot access apis.

Redux Folder
Redux Folder

Useful hooks and higher order components put in the appropriate folders hooks and hoc.

Hooks and hoc folders
Hooks and hoc folders

Pages in Next.js are in a special folder pages. All use cases in cat-facts/index.tsx

Pages folder
Pages folder

Problem and solution

When adding data to a slice in the onQueryStarted method, the data simply does not have time to get into the storage before the component that uses it is rendered.

Redefining baseQuery and accept the method through the arguments onSuccess, which will allow us to put data into storage, and then complete the download. Do not forget to wrap the call in a try catch to see errors in the console.

The code
An example of using slices and api methods
slice
slice
api
api

It is necessary to constantly write loaders that would show the page loading, and then draw the received data.

We create a special “hawk” that will make requests and display messages in case of an error. Its logic is quite complicated, so refer to the project code. Located in the same folder src/hoc. You can adapt it to your project architecture.

For those who understand TS

Typing when used works fine, however, I could not get rid of the crutch when converting to any. If someone knows how to solve, please throw the Merge Request into the repository.

crutch
crutch
hoc folder
hoc folder

If 2 or more requests are needed, then you have to write the logic separately and combine the requests. And if one of them fails, write the logic of a repeated request.

Before using the method, we create a page that will draw the data. To use data from requests, we combine the types of our resolvers and inherit from them.

Data rendering page
Data rendering page

Next, we use “hoki” to process requests to api:

Use
Use “hawks”

withQueryResolver – contains the most important request, which can then be accepted through arguments and use the data. Can only be called once and before withOtherQueryResolver

withOtherQueryResolver – contains a secondary request that cannot be accepted through arguments. It is usually used to put some additional data in storage (slices). Must go after withQueryResolver. Can be called one after another several times. In case of an error, only those that failed will be reloaded, and not all at once.

withMutationResolver – is responsible for processing the mutation, showing the loading modal, outputting the modal with an error (if necessary). Can be adapted to your project.

As we can see, the process of loading and combining requests takes only 2 lines, and connecting logic with mutation processing is only 1 line.

And finally, we prescribe the page that will be exported to the external:

All arguments are concatenated for each hook, so duplication can be avoided.

Requesting some kind of mutation can invalidate the tag, causing the entire page to be reloaded, even when it’s not necessary.

Since we get data from slices, we do not need to make a request again, so we call the method disableReload before the mutation request. In this case, upon a successful call, our tag is invalidated, but this will not trigger a page reload.

Method showRetryModal we use it if we want to show the modal in case of an error that the request failed and offer to repeat the request.

Outcome

With this approach, it is possible to create a growth-resistant architecture in the project and facilitate development. Let’s see what we have achieved:

  • When adding a new page, we need to write only 2-3 lines and the request processing logic is ready. Further, it remains only to write a control that will already work with this data. Now you can not be afraid that with some unexpected error, instead of a beautiful error message, we will have a white screen or something even worse.

  • If some api method is changed, the “hoks” will start swearing that they have been given a controller that receives completely different data than the api hook. This means that the probability of releasing a release with bugs decreases significantly, because in production this will not compile.

  • By combining arguments, they can be used both in the receiver component and in other api methods. For example, migrating one method argument to another method is a matter of 3 seconds. Adding some new argument is also less painful, because if it is passed for some other api method, no changes are needed and typescript won’t scold.

  • If we have enough data from the hook, we can not create a separate slice for the data and, accordingly, do not write extra code. For example, if our page just displays data, why write a separate slice for this? You can simply take the finished data and display it.

  • All error handling logic is in one single place. In the future, if necessary, we can easily add the collection of some statistics or something else.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *