Home > Software design >  What's the actual need for RTK Query when fetch API is provided?
What's the actual need for RTK Query when fetch API is provided?

Time:05-22

Initially the idea of Flux is pretty obvious. The view gets Actions and then emits events based on the Action with which the store is updated. However, when I read through redux-toolkit it seems there are multiple ways of achieving something that should be trivial, namely getting JSON data from an endpoint:

  • Use the fetch API to get some data from an endpoint
  • Use RTK query to do the same
  • Use the React Context API
  • Using the useEffect() hook

Why are there so many different ways for achieving the simplest task?

Thanks.

CodePudding user response:

This question hides a lot of sub-questions, so I'll try to answer them all as per my experience ( I'm partially a fan of RTK Query ).

  1. To ask for a resource via HTTP from a browser there are not many ways, you have to use xhr or fetch API. xhr is the old callback-based API, fetch is the modern standard promise-based API.
  2. On top of these APIs many libraries were born to make the process and configuration easier/wider, today you mostly will work directly with fetch or with some gold-standard libraries like axios which is pretty similar to fetch but it has some cool extra features to handle the fetch calls and configuration.
  3. On modern frameworks/libraries like React the problem is not How to retrieve a resource via HTTP, but how to handle the asynchronous call and the side effects that the response creates to the whole application.
  4. So now this opens up to the several options you enlisted.

Basically for very basic fetch calls, you can handle the call inside a useEffect if it's a GET call that has to retrieve data to show inside the component that just mounted automatically, or imperatively start the fetch on a button click for example if it's a ( usually ) POST,PUT, ecc... call. You can then save the result in a React state, and furthermore pass it throughout your application with a context to let other components access the received response. By the way this leaves you with a lot of other behaviours uncovered, the first one is the loading state during the call, and the error management in case of bad status response or other exceptions. That's surely something you can implement briefly with a useState or better a useReducer hook, by the way, so that's not the real reason why you will feel the need for some async calls manager like RTK Query.

REVALIDATION

What happens if for example you are doing a post call on login of a user that returns the user object with user data, you save that data in a client side state manager ( or React state ) and use that state to display user data in your whole application. During the usage of your application, the user can perform some operations that change his data on DB, so you have to take care everytime he does that, to refetch his data and update his clientside data object, or to return the new user object from each POST/PUT/PATCH etc... call and update the clientside state which is pretty error prone, since you might find yourself having an unsynced clientside/serverside data state if you forget to update the clientside state after each operation, or if there's some issue during these phases. This can often lead to pretty hard to manage and to debug situations. I'd say this is the First good reason to start using a Fetch manager like RTK Query. This manager implements a pretty straightforward Tags based revalidation mechanism, that let's you to assign a tag to each Query ( GET ) that you perform, so for example you will make a query when the user logs in that retrieves the user data and you will tag the data with providesTags: 'User'. Now you can safely perform any number of mutation on your user data on backend, and to each one, clientside, you will tell that as soon as it gets a response, it has to revalidate a certain Tag ( or multiple ones ). For example you might have an editPasswordMutation that changes the user password inside the database, and you will just add a invalidatesTags: 'User', this means that as soon as the user receive a positive from the server confirming that he updated the password, the client knows it has to reexecute the getUserData call, get fresh user data, and save them into the redux global store, so they will be immediately accessible to all the application components that are consuming them. Totally authomatic. This whole mechanism would definitely need you to write much more code if you were to implement it yourself.

OPTIMISTIC UPDATES

Most of times you will find yourself not wanting yor UI to wait for server response before showing the effect of a ( for example ) button click. Imagine when you click a thumb up on Facebook, you immediately see it in your UI, and that's not cause they have super fast servers, but because the UI is assuming that the call succedeed as soon as you click the button so it instantly adds a 1 to the likes count of the showed post. This behaviour is not trivial, since usually when you show data in your application you have and should have a single source of truth. So for example if you are rendering a list of blog posts, you should have retrieved them from your backend database, saved them in your clientside state manager or directly render them as-is. If the first post has, let's assume 10 likes, you will render them based on data.posts[0].likes.count. So that's your source of truth. If you want to add a Like/Dislike button, and you want to implement an optimistic update, you would have to manually alter that source of truth, which is not good at all since it could be shared across multiple components ( in case of a global state ) or to duplicate it, and alter the duplicate, but that's not good either. It's almost never ( almost ) a good idea to duplicate a source of truth. RTK Query to the rescue! With RTK Query Optimistic Update is pretty straightforward to set up, basically EVERY fetch can implement an optimistic update behaviour, since you can use the life-cycle method onQUeryStarted to intercept the immediate start of a call, and then use the utility : updateQueryData to update the data that your mutation is going to invalidate. Extremely SMART. So in this case you will just have a simple mutation that will add a like to the blog post, that mutation will invalidate the posts data, the single post will be immediately updated in the UI with a 1 like, meanwhile the server will refetch the posts list ( since they were invalidated ) and fresh data in sync will be retrieved, and the user will notice just a pretty smooth experience.

There are many other features, especially involving Cache management, which are a plus, ( even if sometimes they can create some issues ) but I highlight these two main features that alone pushed me to become a user of RTK Query, it might look a bit verbose at start, but it definitely makes things pretty clean and tidy, coherent with the Redux philosophy.

CodePudding user response:

As per docs, the goal of the tool is

It is designed to simplify common cases for loading data in a web application,
 eliminating the need to hand-write data fetching & caching logic yourself.

It provides some caching and I personally find it very convenient to have declarative isLoading/error without the need to useState for all of it every time : )

But again, it's only optional: RTK Query is an optional addon included in the Redux Toolkit package.

There are multiple ways to achieve the same goal indeed. There's no gold standard in React world regarding this, for better or worse.

  • Related