Everyone’s using React Hooks these days, functions are in and classes are out. No more this yay! (One of the reasons the React team purportedly introduced them). They allow us to easily “mixin” functionality into our component and elegantly execute code when dependencies change. They’re a nice idea.

But sadly we’ve traded “cool” for performance, and simple solvable problems like this context for much more nuanced and complicated ones.

Stale memory

A problem we rarely had to worry about in the past, but take a simple callback which greets a person:

const [greeting, setGreeting] = useState("hello");

	const cb = useCallback(() => {
	  alert("greeting is " + greeting);
	}, []);
view raw
staleMemory.js hosted with ❤ by GitHub

If we later change greeting the callback is now referencing an old value. If this was a 2mb image that would also be a 2mb memory leak. Hooks have introduced one of the two hard things about programming: cache invalidation.

Infinite loops

“Add it as a dependency” the hook experts say. So let’s fetch the next fib number and set it to state:

const [fibonacci, setFibonacci] = useState(0)

	const fetchNextFibonacci = useCallback(async () => {
	  if (fibonacci !== MAX_FIBONACCI) {
	    const nextFib = await fetch(`/api/nextFib/${fibonacci}`).text()
	}, [setFibonacci, fibonacci])

	useEffect(() => {
	}, [fetchNextFibonacci])
view raw
infiniteLoops.js hosted with ❤ by GitHub

Now the fetchNextFibonacci function changes after every fetch due to a dependency on fibonacci and re-runs in an accidental loop.

This seems contrived but happens in any situation where you want to both read and set state in a callback called in a useEffect, such as fetching some data on mount.

Slower render reconciliation

Most code I’ve seen rarely wraps functional components in React.memo and we’re encouraged to write naked functions inside render which will break any purity checks as they create a new function every render.

We’re told not to prematurely optimize and that it’s probably “fast enough”, because dealing with memoizing all your functions is not pretty, more costly to initialize and execute, and not trivial due to gotchas that require expert knowledge of JavaScript and hooks (we’ll get to this).

When you create methods or variables in classes they are created once per instance. It doesn’t require someone with deep knowledge of JavaScript to combine this with PureComponent to have a very efficiently updating app.

For any callbacks you do handeClick = e => { ... } and voila you’ve both solved any confusion around this and also have a “memoized” callback which didn’t involve the nightmares of useCallback.

And this gain which many people tell you not to worry about really can make a significant difference in large apps or components which render lots of elements (e.g. data grids).

#react-hook #javascript #react

The Ugly Side of React Hooks
8.80 GEEK