Gotchas of working with React Hooks and Concurrent mode

Stefan Nagey
Capbase Engineering
4 min readJan 12, 2019
React Hooks make things better, but there are still Gotchas to React Hooks

While in this series, I’ve painted a very rosy picture of the functional world of react hooks, including part 1: why do we care about hooks in the first place; part 2: how to manage state in stateless components when working with react hooks; part 3: how to work with complex states and stores from redux with react hooks and stateless components; and part 4: asynchronous functional programming with the useEffect hook, it’s not always sun and roses when working with hooks. There are a few key issues and “gotchas” that you should be aware of in this paradigm, and this piece will explore those fully.

Gotchas of Hooks, and How to avoid them

As mentioned earlier in the series, when working with hooks, you have to be careful to use them only in the main function body.

Hooks cannot be used in any conditions, and can most certainly not be used in a loop.

The reason for this is to make sure that the hooks being used by a component can be statically analyzed and optimized. Hooks are also (relatively) expensive operations, so you don’t want to be storing millions of states when you could have just stored those same data in a single struct.

Luckily, there is an eslint rule that will help you keep track of this.

React Concurrent Mode and Hooks: A Word of Caution

While everything that we’ve discussed here can be seen to be mostly linear in nature. Once concurrent mode comes into play, this isn’t necessarily going to be the case. For example, you wouldn’t want to necessarily initialize a context in a component with items that are set from a higher up context.

That was confusing, let me explain.

Let’s say that you have a component that does a back end call, sets some data in a context, then redirects to another route.

In the non-concurrent world, each of these things would happen in order. First your dispatch would complete, then the <Redirect /> component would render. Then, in the component to which you have been redirected, you would have the result of the dispatch method available in the context on the first time the function is executed. As a result, you could use this as an initial state to open some internal context in that component.

With concurrency active, it will execute your component downstream of the Redirect as soon as Redirect is first called (i.e., React won’t wait for the Redirect component to render and for the dispatch to complete).

In almost all cases this won’t be a problem, unless as mentioned, you’re using this as a part of a state that wouldn’t necessarily be tracked the same way.

You can get around this by making sure to always use reducers in conjunction with your contexts, and to always set your initial arguments as undefined, or something else that will let you know it’s not quite ready.

Another React Concurrent Mode Gotcha (as of today)

Due to the way the concurrent mode is implemented (i.e., it’s safe for functional components, less so for object-based ones), it has the side effect of setting React Strict Mode.

I don’t want to say that “most” npm react packages are not Strict Mode safe, but, well, most npm react packages are not strict mode safe. We can hope that by the time that concurrent mode reaches release many of them well be (e.g., react-router currently has its next tag as strict mode compatible); one should not be confident that necessarily all packages will get there in time (looking at you react-intl from yahoo…). Even React.lazy has some unintended side effects when running in strict mode.

Concurrent mode can definitely make things feel snappier, but at this point, it’s probably safer to just stay way.

Wrapping it all up with a nice bow

React hooks present an amazing new set of functionality that allow React to be written in a completely functional manner, and open the door to all sorts of possibilities for performant (fast), efficient (low memory usage), and responsive (non-blocking / concurrent) web applications. We’ve looked at why functional programming in React is important; how you can manage state when writing stateless react components; how you can replace redux’s connect in a functional world; and how to handle asynchronous tasks when working with functional components.

I hope you enjoyed it, I’d love to hear what you think, and what I might’ve messed up.

About the Author

Stefan Nagey is Co-Founder of Capbase and Dharma. In a previous life he has variously promoted motion pictures, been a diplomat and registered foreign agent, and run payment processing in the online gambling space. He variously writes about engineering and the headaches of being a startup founder in the 21st century.

--

--

Stefan Nagey
Capbase Engineering

Data geek. Capbase.com and Dharma.ai Co-Founder. Passionate about scalability, governance, cycling, and poker.