How to use separation of concern with react-query (in a clean architecture context)

Viewed 791

I'm currently thinking about the perfect architecture for my professionals projects needs. I read a lot of article about (clean) architecture and I got to the point were I think that I want my UI managed with React totally separated from the application business logic that will be managed by "application manager". The issue is that I want the "application manager" to config and trigger mutations (I think get queries can be used in components without any issue). But since react-query require it to be in React component by using hooks, I don't think it is possible.

I am wrong ?

Does it exist a workaround ?

Maybe you have a library that manage that better ? I'm thinking about RTK Query maybe...

1 Answers

I am a heavy user of RQ for quite some time and since architecture question can never have an objectively correct answer, I can demonstrate what I do personally.

First, I extract all queries and components into API modules by domain, given a simple app with posts, authors and comments, I would have files along these lines with those exports:

// apis/posts.js
export function useGetPosts() {}
export function useGetPost(postId) {}
export function usePutPost() {}
export function usePostPost() {}
export function useDeletePost() {}

// apis/comments.js
export function useGetComments(postId) {}
export function useGetComment(commentId) {}
export function usePutComment() {}
export function usePostComment() {}
export function useDeleteComment() {}

// apis/authors.js
export function useGetAuthors() {}
export function useGetAuthor(authorId) {}
export function usePutAuthor() {}
export function usePostAuthor() {}
export function useDeleteAuthor() {}

Each of those modules would internally handle everything necessary to work as a whole, like useDeleteAuthor would have a mutation and also modify the cache on success, or possibly implement optimistic updates.

Each will have a system of query keys so that the consumer (your components) don't have to know a thing about them.

function MyComponent() {
  const posts = useGetPosts()
}

function MyOtherComponent() {
  const deletePost = useDeletePost()
}

Try to make the APIs as complete as possible, but also don't forget that mutations can, for example, accept callbacks on call-site:

deletePost.mutate(payload, {
  onMutate: () => setState(false)
})

Let's assume you can use this to for example close a confirmation modal before deleting. Something like this doesn't belong to API module, so we just provide it as a local callback to the mutation.

As stated above, there is no correct answer. There is definitely an argument for doing it the other way round and using collocation more, putting queries next to the components where you are using them. But if you want separation, this would be a place to start in my opinion.

As Ben wrote in the comment to your question, RQ is just hooks, so I agree that trying to put it "outside of react" is non-sensical.

Related