feature-sliced / documentation

🍰 Architectural methodology for frontend projects
https://feature-sliced.design
MIT License
1.41k stars 147 forks source link

How does React-Query fit into the application structure #553

Closed HasanMothaffar closed 1 year ago

HasanMothaffar commented 1 year ago

Thank you for such an awesome repository!

I noticed that recently, many React applications are making the switch from state managers such as Redux and MobX to using React-Query. I was wondering how does this library fit into the application architecture that FSD suggests?

Looking forward to a stable v2!

EliseyMartynov commented 1 year ago

@HasanMothaffar First of all, thank you! From my point of view, react-query is not about state management but about cache. If you're building your app only with react-query, anyway you have to have a place for a state of your app for some slices of code (through Context, etc.). Furthermore, FSD doesn't force you to use any technology. Therefore, your model could be implemented in any ways you prefer. If react-query fulfills your requirements and you don't need any state (in a classic way) at all, you can take a look at something like api segment in your entities which will contain react-query hooks that wrap your abstract api calls from shared/api.

illright commented 1 year ago

I'll take the liberty of expanding on @EliseyMartynov's answer in hopes that one day we turn it into a page in our docs :)

Indeed, TanStack Query replaces most of the need of storing "server-side" state that commonly populates the model segment of Entities. However, since it is indeed more about querying than storage, I'd say the rightful place of useQuery hooks is the api segment. The model segment of a particular entity in this case can be empty, or it can store additional client-side state through whatever storage library/mechanism you prefer. However, placing the rest of TanStack Query's infrastructure is the interesting part. I'll go about it layer-by-layer.

Shared

Usually, shared/api contains the barebones API client, not aware of any entities or business logic. If you have wrappers around fetch, axios, ky or any other request-making library, they go here.

If you'd like to build a query-key-type-safe version of the useQuery hook (unexplainably awful, very fun, heavily recommended), you can also place it in Shared, since it's a good layer to store types to side-step the limitation of entities not being able to import other entities.

Entities

Unlike other server data storage solutions, TanStack Query manages it transparently, therefore, for the most part, we can do away with the model segment. The api segment takes the burden, and there are two approaches here:

  1. Define the query function inline, right in the useQuery call:
    export function useUsers() {
     return useQuery(["users"], () => fetch("/api/v1/users"));
    }
  2. Define the query function and the hook separately:

    // entities/user/api/use-users.ts
    import { listUsers } from "./list-users.ts";
    
    export function useUsers() {
     return useQuery(["users"], listUsers);
    }
    // entities/user/api/list-users.ts
    export function listUsers(): Promise<User[]> {
     return fetch("/api/v1/users");
    }

You may want to consider the second variant if TanStack Query is not the only way you use your API methods (for example, if you also make requests through Cypress).

Features

The mutations that your features need can go in the api segment of a feature slice. If any additional client data storage is needed, as with Entities, you can use the model segment along with your preferred data storage solution there. The point about separating mutation functions from mutation hooks also applies here.


That's what I've gathered from my experience with FSD + TanStack Query, let me know if you have more questions about it. Your inquisitiveness will help other people in the future :)

HasanMothaffar commented 1 year ago

Thank you both for taking the time to write a detailed response! It's indeed helpful. I agree that React-Query is only responsible for the API cache layer, I might have written my question in a haste, sorry.

I'll look more into these ideas and apply the ones that fit into my application.