React hooks for advanced use cases

Blue Harvest
Blue Harvest Tech Blog
5 min readDec 14, 2018

by Kalle Karamell, Senior Software Engineer at Blue Harvest

In this blog post, we’re going to look into the caveats of React Hooks. Sit tight and we’ll get into the details.

So what are React Hooks?

React hooks is the cutting-edge alpha feature that all the smiling Facebook-engineers “can’t wait for” and might arguably be the most significant addition to React announced in a really long time. This might be the start of a front-end revolution! According to the roadmap this will ship by Q1 2019. The hooks api allows you to write a complete app only by composing functions. This means that…

  1. Your code will be free of the JavaScript legacy/counter-intuitive “features” like class, this, prototype, etc.
  2. By only using functions we can design very modular and testable code.
  3. We resolve some scalability issues imposed by limitations in the current design, such as the complexity of higher order components or render props (in theory, we’ll get back to this one).

Applying this to a real-world problem

Since the use-case of React Hooks is explicitly catered towards solving particularly complex problems, we decided to try it out with a tricky component from our website. It’s the carousel from our Blue Harvest website:

Our carousel component, up for refactoring to Hooks

This component is special in a number of ways

  1. We use render props to separate concerns. There are 3 different parts to it, 3d rendering/positioning of the images based on the scroll offset, and the physics engine with snapping points to determine the scroll offset. We also need a glue composing these components along with arrows to go left or right.
  2. We need smooth 60FPS for the rendering which we’ve optimised by returning false from componentShouldUpdate and instead mutating the DOM directly to avoid React diffing.
  3. We need to utilise requestAnimationFrame whenever the touch is released in order to perform an animation to continuously update the offset.
  4. The currently focused element can also be changed from the “outside” by using the UI left/right arrows. We chose to avoid “lifting state up” which means breaking the independence of the scroller component. We therefore expose methods in the render props callback on the scroller component to scroll one step.

The architecture of the components looks a bit like this diagram:

After a lot of tinkering with React hooks, we were able to refactor the component to something much more modular:

You can see that the new version has smaller, re-usable components. We were really happy with these results. You can compare the two versions of the component here. However, it wasn’t all sunshine and rainbows in the journey of getting here. There were several obstacles to surmount.

React Hooks are not for beginners

Early on, I was intrigued by the fact that I could write an effect-based function useAnimationFrame to “write once, use everywhere”. Finally, I can avoid writing a requestAnimationFrame boilerplate instantiate a callback etc.

Being a hooks beginner, I hit a wall here. Many of my attempts ended up in infinite call recursions, buggy, or highly sub-optimal performance. I ended up reaching out to the community of React maintainers, and quickly got help in my filed issue. The solution I got works well, but is not easy. Put on your reading glasses and study the code below.

const useAnimationFrame = callback => {  const callbackRef = useRef(callback);  useMutationEffect(
() => { callbackRef.current = callback; }, [callback]
); const loop = () => {
frameRef.current = requestAnimationFrame(loop); const cb = callbackRef.current; cb();
}; const frameRef = useRef(); useLayoutEffect(() => {
frameRef.current = requestAnimationFrame(loop); return () =>

cancelAnimationFrame(frameRef.current);
}, []);};

This code is utilising the more esoteric spectrum of hooks. The features useLayoutEffect, useMutationEffect, useRef, are in the docs referred to as “needed specific edge cases. Don’t stress about learning them up front”. Personally, I felt like setting up a requestAnimationFrame loop was going to be something simple and was a bit disappointed to realise I have to dig into the nitty gritty details just go get this working.

It also turns out that React refs are not only for interactions with the DOM anymore, contrary to what the docs still state to this date. From a hooks perspective, on the other hand, Refs should be thought of as “instance variables”, as explained in the hooks FAQ.

React hooks are harder to optimise

We couldn’t really figure out exactly how to modify the DOM directly without performing React tree diffing, but it’s suppose to involve useMemo in combination with some condition to optionally update the DOM. However, even keeping the original optimised code for rendering, hooks were slower. Although there was no noticeable performance difference when testing normally on my MacBook Pro, the carousel was about 5–10FPS slower when enabling CPU throttling in Chrome DevTools. Given the black magic we had to put into some of the code to make it work, I had literally no clue on how to optimise this. Of course, it might be that the React team will optimise hooks more as the new API matures.

Where does this leave us?

React hooks are explicitly catered to simplify over-complicated workflows and limitations on the current API. When we refactored a complex component to use hooks we end up with slightly slower, slightly more complex code.

Therefore, our conclusion is that today, React Hooks are only for the adventurous, and not for production. However, the promises and rationale behind React hooks are very viable and it’s definitely possible that React hooks could be a big change in the long run for frontend development.

Finally, there’s probably a good reason why React is advising against refactoring our old components to use hooks instead of classes. When hooks are more mature they might be considered just another tool of that will live alongside the old API, and you can choose your weapon depending on the nature of the problem you have to solve.

--

--