Private labels are needed! Is logic in hooks bad?

Hi all! I'm Nikita Karpenko – leading frontend developer of the CorelK team in Cloud.ru. Today I’ll talk about why logic in hooks is Always sometimes it’s not very good how not to make a mistake with the choice of technologies and manage the state of applications in a way that is convenient. In this article I will share how I work without logic in hooks. If interested, look under the cat!

What exactly is the problem? Let's look at a simple example

I'll start with a simple example: a customer asks to make a sign with users:

For the front, this is a simple and understandable task: request data using React Query – done!

But the customer comes to us again: “There are already 200 users on the tablet. Please do a search.” No question – we did it:

And everything seems to be fine, but the useUsers hook is now one level higher. Now, for every change in SearchValue, our UsersTable component will be redrawn, although we did not plan for this behavior:

What do we usually do in such cases? That's right, we use React memo. But at such moments the question may arise: why should we even think about this when solving the most primitive problem?

And now the customer comes to us again and wants us to add filters, pagination and search with debounce:

Okay, let's write some code. But here some nuances appear: we have to use useCallback so that our function saves the link during rerendering. And again questions arise in my head: why should I think about this? Are we doing everything right if solving even such a simple problem forces us to take into account so many nuances?

There was a lot of logic and variables inside our component, and we didn’t even implement everything we wanted:

But there is a way out – you can put everything in a hook. This is what it will look like:

Great, we've taken the logic out and now it's outside the component. But the hook is still nailed to the lifecycle. This can be inconvenient, because not all of our logic depends on the component’s life cycle. Therefore, sometimes we get hung up on “symptoms”, for example, we catch a transition to a page on the mount of a component, and not as an event on the router.

It turns out that when we just start working on a project, we are dealing with “CRUD mixers”. We get data, draw it, then get new data, add it and draw it again: simple and primitive.

But everything changes when we start making complex applications where there are a huge number of entities and many times more connections (about the same as the number of passengers and lines in the Moscow metro).

For example, we make chats, large and complex forms, dashboards, and not just plates and forms with four fields.

But how do you make complex applications using local states? It seems like it's too hard, Props Drilling appears. The question arises – what to do?

Options for solving the problem

We have several options:

  1. Use react-query + context. The downside of this approach is that you need to migrate data between several reactivity systems, which is not very easy. Also, we won’t be able to simply describe a business process, because the logic will also be inside useEffects (I think you’ve seen useEffect hell, where it’s impossible to get rid of them).

  2. Use STMs that help manage both local and server state. For example:

  • RTK Query is a good option, if not for Redux;

  • Effector + Farfetched is a library of Russian-speaking developers that is very declarative, provides a huge range of tools and allows you to solve business problems;

  • many other solutions.

I suggest looking at how Effector + Farfetched works.

Benefits of Effector + Farfetched

So, in the first screenshot our component:

And on the second, the code that needs to be written in order to take the logic from useState aside:

You may say: “What a lot! Previously, in order to create a fortune, I needed to write exactly one line, but now I’ll obviously have to write more!” But this can also be corrected – make a factory that will also generate in one line.

So, we want to make a request to the server every time the filters change with some kind of debounce. What it will look like: We are putting together a common filter that will be based on all the related ones (i.e. all the filters that we had). Now a request will be sent for every change. We also have debounce, which allows you to implement in one line what would take about 15 lines using hooks:

See what the UsersTable component looks like now. Receiving data has returned to the table component, and all filter states are used only in those components where they are really needed. At the top level there are no states that force the entire application to be rerendered.

Now look at the first screenshot and the second. The first one has a super clean structure of components that do not depend on each other in any way and do not force each other to re-render. We don’t need to think about what to wrap in useMemo, useCallback – we have taken all the data into static storage, which will depend on the life cycles of components only when we want.

But there is one catch – farfetched. It has about 1,100 downloads per week (approximately 5,000 per month). It seems that using a library with so many downloads could be risky in some cases.

But there is a solution for this case too) And it is quite simple:

You can take tanstack/query-core (not react-query) and add the logic that we described on top of it. Now it will not be nailed to components by hooks:

conclusions

  1. It is worth remembering that applications always become more complex over time. The system must help the user solve his problems; this entails complex processes rather than simple CRUD operations. This is important to consider when choosing a stack before starting a project or major refactoring.

  2. STM is not as painful as it used to be. They don't force you to write a huge amount of code, as was the case in the days of Redux.

  3. Little code does not mean good code.

Nikita Karpenko's report in video format, as well as discussion and answers to questions, can be viewed at YouTube channel Cloud.ru. Subscribe!


Interesting blog:

Similar Posts

Leave a Reply

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