Photo by Samuel Zeller on Unsplash

Recently I decided to experiment with how I write React applications. Usually this involves Redux, sometimes Apollo or some boilerplate reducing scaffolding and I like all of these but I always feel like the experience could be better.

What I wanted to do is something that would have small modular parts I can combine as I needed. I wanted to start simple, just with data fetching, without any global state and just pluck it in later on. I wanted to still use redux underneath so I do not need to reinvent all the tooling around it. I also wanted redux to be still usable in its raw form for more complex state management or as a migration path.

So I started working on react-sync-tools as a set of HOCs that would meet these vague requirements. It is still work in progress and I can’t say I am fully satisfied with it but at the same time it provides subjectively better interface for some of my small projects, than most of other solutions I tried. Apollo is in some ways similar but sometimes you just can’t or don’t want to use GraphQL and I have other nitpicks with it.

In the meantime though React introduced Hooks in their 16.7.0 alpha and I realised that with them, the code I was working on could be much simpler and that some parts will probably even disappear. So I got hooked like the rest of the internet and jumped on the opportunity to try them on some non trivial code.

Typescript sidenote

It is some time since I worked with typescript so doing a small library in it seemed like a nice way to get familiar with it again and see how it progressed. TypeScript can be pretty complicated as exemplified here. It is also not fun debugging errors like this:

and sometimes I feel like I need a separate debugger for the type system to understand what is going on.

Even when you get things right it is still possible that some 3rd party library will cut some corners somewhere and your types won’t survive contact with it.

When everything is right though it feels pretty good to get errors when you mistype a plain string if it does not match your expected prop type in the wrapped component.

As I mentioned I wanted something modular and that is easy for the simple stuff, while being able to grow easily just by adding components. For me that means I want to be able to do network fetch from a component without hassle for a quick prototyping, before getting into how to store it and just add state management later when needed.

withActions is a HOC for that, you supply it with an object of Promise returning functions and you will get each wrapped in an object with isLoading , response , error and run properties. This way you do not have to handle cancellation, checking whether component is mounted or local state changes yourself.

withModel is another HOC to give you access to predefined model which is saved in your redux store. Your wrapped component will get current state and any reducers defined for your model.

You can easily chain those two so that your actions can call models reducers to store the response or to check beforehand if the action is needed. You can easily compose this into a new HOC and reuse it as needed.

react-sync-tools usage example

You can see the code here.

Obviously hooks are still experimental at the moment and so are all the things I did with them.

From the start, hooks seem to be a much better fit to what I wanted to do than HOCs (or render props for that matter). They compose much easier. Do you have multiple hooks you use together often? Just wrap them a function. That’s it. People tend to focus on the rule that you can use them only in top level but that haven’t been a problem even when you start composing them. When you think about them as defining a class method it does not seem that strange anymore.

This simpler API also means it is much easier to type. You go from factory functions creating HOCs, to simple function taking arguments and returning something new.

One thing I was worried about was having to provide both hooks implementation and HOCs or render props components with new libraries. Except for more complex types it is really easy to create HOCs from hooks. For example the withAction HOC based on useAction hook looks like this (with some omissions for clarity):

You can see the react-sync-tools-hooks repo here.

There are some caveats with the hooks, that can be confusing at the start.

Hooks are executed during render, which makes sense. They are simple functions, there is no magic and they are not transpiled to some different code or a class component. They just call some React internals and ask it to store some value, retrieve it or run something at a specified moment. But that also means anything that runs in a hook cannot really affect current render, except for throwing some error or provide value that would change what and how is rendered. That means we do not have useShouldComponentUpdate hook. Even React.memo is not a hook but a decorator (or higher order function).

There could be some updates as this is recognised by the React team and there is a discussion going on about it.

It also means that you are running your hooks on each render. So if your hook is doing something expensive you should make sure you memoize it. Some of the hooks that React provide already use memoization based on array of values that you pass as the last argument to them. If any of the values change, they will recompute and return new value otherwise they return the same one (see for example the useCallback).