DEV Community

Xuan
Xuan

Posted on • Edited on

I'm developing a new server state management library for React.

I'm excited to introduce the Daxus tool I've developed to the community. In this article, I would like to share with everyone the motivation behind creating Daxus.

In my company, we use Redux with async thunk to manage server state. While Redux brings many benefits with centralized state management, it also comes with some drawbacks. For example, combining all reducers into a single store leads to excessively large initial JavaScript files. Additionally, even with Redux Toolkit, we still need to write a lot of repetitive code. As a result, senior engineers in the company have been considering replacing Redux, but so far, we haven't found a suitable package.

Why not use SWR or React Query?

Actually, we have tried incorporating both SWR and React Query into our internal console-type websites, and colleagues find React Query to be more user-friendly than SWR. Although React Query performs well in console-type products, most colleagues believe it is not quite suitable for our main product website.

Our product is a user forum that receives a large number of user visits every day. Here's an example that colleagues think React Query is not suitable for our product: when a user creates a new comment, we want the corresponding post's totalCommentCount to increase by one. From the perspective of React Query, we should execute the following code after creating a comment:

queryClient.invalidateQueries({ queryKey: ['posts', 'get', postId] });
Enter fullscreen mode Exit fullscreen mode

This way, React Query will automatically request the new post in the background and update the corresponding post. However, considering that our post response is quite large, fetching the entire post just for updating totalCommentCount seems wasteful. You might think we can do it this way instead:

queryClient.setQueryData(['posts', 'get', postId], oldPost => {
  const totalCommentCount = oldPost.totalCommentCount + 1;
  return { ...oldPost, totalCommentCount };
});
Enter fullscreen mode Exit fullscreen mode

But there's a problem with this approach. When the user goes back to the post list, the totalCommentCount on the list won't update because the queryKey is different. This may appear odd to observant users. Of course, we can add more code like this:

queryClient.setQueryData(['posts', 'list'], oldPosts => {
  const oldPost = oldPosts.find(post => post.id === postId);
  if (!post) return oldPosts;
  const totalCommentCount = oldPost.totalCommentCount + 1;
  const newPost = { ...oldPost, totalCommentCount };
  const oldPostIndex = oldPosts.indexOf(oldPost);
  const newPosts = [...oldPosts];
  newPosts.splice(oldPostIndex, 1, newPost);
  return newPosts;
});
Enter fullscreen mode Exit fullscreen mode

This way, we take into account the scenario of updating the list. But is it really that simple? Our list can have various forms, such as "popular," "latest," and different forums with their own lists. The queryKey might look like this:

const allPopular = ['posts', 'list', 'popular', 'all'];
const allLatest = ['posts', 'list', 'latest', 'all'];
const forumPopular = ['posts', 'list', 'popular', forumId];
const forumLatest = ['posts', 'list', 'latest', forumId];
Enter fullscreen mode Exit fullscreen mode

If we also consider all these scenarios, it might bring us even more mental burden than using Redux, not to mention some API responses have this format:

{
  "items": [],
  "nextKey": "123"
}
Enter fullscreen mode Exit fullscreen mode

If we have to mutate the data using the methods mentioned above, it would be a disaster. Moreover, it goes against the practical way React Query recommends us to use. While React Query fits well with console-type websites, unfortunately, it seems less suitable for our main website.

You may think that we can use queryClient.setQueriesData to set all lists, but it will make things more complicated. Moreover, the maintainer of RQ doesn't like use this too. (See here)

So, what makes React Query unsuitable for our main website? I believe it's the level of control over the data. React Query focuses on managing server state for us, which means we don't have as much control over the data compared to using Redux. When using Redux, updating a post would automatically update the corresponding post in the list. However, when using Redux, it's not as straightforward as using useQuery to retrieve the data. We need to write a lot of actions and reducers, and if we want to add features like deduplication and revalidation, the amount of code to write increases even more. Clearly, Redux is not the optimal choice.

Since we haven't found a suitable package for our use case, why not develop our own? This brings up the issue of maintainability. If we create a tool that only we use, then the responsibility of maintaining it falls solely on us. Lack of community support is a significant concern for senior colleagues.

As a junior developer, I have always been interested in state management problems. Therefore, I want to try developing my own tool that meets the company's needs as my side project. Of course, I also hope this tool can help other developers who are struggling with managing server state.

Goals to achieve

First and foremost, it is essential to empower users to have full control over their data. Unlike React Query, where server state management is handled for us, all data writes will be user-defined. Although this may require users to write more code, I believe it is a necessary trade-off, and compared to Redux, the amount of code to write is relatively less.

Another crucial point is to provide a concise and user-friendly hook, similar to useQuery, that allows developers to call it from any component without worrying about duplicate requests. Additionally, features like polling and revalidation are also important.

If you have any ideas or suggestions regarding this project, please feel free to share them with me. Thank you.

Here is the repo of Daxus: https://github.com/jason89521/daxus

Top comments (6)

Collapse
 
karaca19 profile image
Bora Karaca

Solid points. I tried to implement redux on my project for a use case like this one. Although I got the functionality I wanted, the code became a bit repetitive. I really like the idea of "creating a separate model with different types of data structures for each data". Will definitely give this a try after my exams :D

Collapse
 
jason89521 profile image
Xuan

Thank you! I look forward to hearing any feedback from you.

Collapse
 
chideracode profile image
Chidera Humphrey

I really commend your problem-solving skills.

Collapse
 
jason89521 profile image
Xuan

Thank you for your commendation, and any suggestions or feedback you have for this project are also welcome. šŸ˜€

Collapse
 
anarno profile image
Anarno

What about Valtio? Take a look: github.com/pmndrs/valtio

Collapse
 
jason89521 profile image
Xuan

Valtio is not specifically designed for server state management, just like zustand, jotai, and recoil. Therefore, it may not fit our use case.