toc

React Hooks Course

Table of contents

To be able to get the most out of this course it’s best if you are already familiar with JavaScript, React and Typescript, although not necessary to follow along, is strongly recommended.

Motivation

In the words of the creators:

  • It’s hard to reuse stateful logic between components
  • Complex components become hard to understand
  • Classes confuse both people and machines

If you want to read up on the “why” of hooks you may do so by following this link.

There’s even an introductory video on hooks by Dam Abramov

Translating class patterns to functional components with hooks

State management

setState

useState

We immediately notice that the example relying on useState is slimmer. For example, we didn’t have to declare the type of the name state since it was inferred based on the initial value which was once again ''. We invoked setName with the new value for the name state instead of passing it an object.

However, there are a few things to note here. When setName is invoked the HelloContainer component is rerendered with the value passed to the setter. This means that even if initialValue had changed by the time of the rerender, it would not affect the name state.

Wait a second, I got a call from the product owner. He said that the new requirement is that the greeting rendered by the Hello component should not change immediately as the user is typing into the input, but should instead update only when the user submits the update. Fear not, once again we can tackle the challenge using the useState hook. Let’s delve deeper into exploration by answering the question of how to handle multiple state fields.

Multiple state fields with setState

Multiple state fields with useState

This was fairly easy. However, you might ask: Why did we have to add another useState hook. This brings us to another notable difference. Namely, the useState setter does not merge the argument with the rest of the state. Meaning that you would have to spread the current state in order to perform a partial update. Hence it is generally recommended to combine your state fields into an object if they are being set simultaneously.

Wait, it’s the product owner again. The new business rule states that greeting should only update a second after the user submitted. And on top of that, if the user updates the value during the second after submitting, the name should be updated with the most recent value. This sounds complicated, but it is in fact easy to implement! Similarly to setState, the setter function which is the second element of the array returned by the useState also accepts a callback function which guaranteed to be invoked with the latest value. Enough of the talk.

Async setState

Async useState

Notice that we invoked setName inside the callback passed to setValue. The alternative would have been to combine the two state fields into an single object. However, this would mean that in order to update either value or name, we would have to spread the current state along with providing a new value for the given field. But wait, it’s not the product owner this time, instead it is a colleague from the development team.

What if the state update logic becomes complex or the state involves multiple sub-values? Is this the point where we reach for redux?

I’m glad you asked my friend, and the answer is no, not necessarily. Let’s consider the following hook which is a close cousin to the useState hook in the next section.

Reduced pattern

useReducer

As mentioned above and as it is said in the official docs:

useReducer is usually preferable to useState when you have complex state logic that involves multiple sub-values or when the next state depends on the previous one.
useReducer also lets you optimize performance for components that trigger deep updates because you can pass dispatch down instead of callbacks.

Now, this pattern is not something which was carried over from the class component pattern, however we can still implement it using class components. And below is an example of what a possible implementation may look like.

Reducer pattern with class components

The above approach is quite verbose however is more scalable. Now let’s pay a visit to the man of the hour, low and behold the useReducer hook.

useReducer hook

First of all, you may have noticed that the reducer function is declared outside out the component and as such it is not coupled to the component. This means that the logic for updating the state is replaced and reused in other places, it is effectively abstracted away from the component. On top that, the update logic is easier to maintain, since adding and updating of individual cases is straightforward. However, there’s more to it as we will discuss in the Creating your own hooks section. In a nutshell, our reducer is a pure function. Hence the missing puzzle piece is the ability to perform side effects. Luckily for us the redux architecture is easily extensible with middleware. In the case of the useReducer hook, we can create quasi thunks and epics and more. Let’s first complete our tour of available React hooks. With a very powerful effectful pattern.

useReducer with reset

As an added bonus, let’s go through an example of how to reset the reducer state.

It’s nothing too crazy.

React Context and Hooks

If useReducer impressed you just wait until you see the next hook which is related to a very powerful React API, namely, the Context API. We’ll soon be exploring the examples, however first comes a practical refresher how to use the Context API. It all starts with the context creation using React.createContext. It’s straightforward, the createContext function accepts an initial value. Based on the shape of the initial value, React is going to determine the type of the context, or the other way around - if you provide a type argument React is going to enforce the type of the initial value and all future values whom you’re going to set the context with.

Component.contextType

Context.Consumer

useContext

Ref pattern

The ref pattern commonly is utilized to access DOM node references. However, a ref object is generic and can hold a reference to basically anything. The reference is mutable and we can reassign values to it over time.

Something which changes over time. But, wait, isn’t the practically the definition of state?! Does this mean that refs and state are the same thing?

Yes, they are similar concepts, however there is a key difference. That difference lies in the fact that mutating a ref does not cause a rerender, unlike setting the state does.

Practically, there’s an significant difference when it comes to interaction between refs and the useEffect hook, compared to state and useEffect, but let’s not get too fast ahead of ourselves.

Now with that behind us. Since class fields are mutable by default , the simplest way to implement the ref pattern with class components is to just declare a class property. Bear in mind that the class field syntax in not yet part of the JavaScript spec, however it is one step away from. To quote MDN:

Public and private field declarations are an experimental feature (stage 3) > proposed at TC39, the JavaScript standards committee. Support in browsers is limited, but ?> the feature can be used through a build step with systems like Babel.

The good news for React developers who are using create-react-app is that we are able to use more than just ES2015 language features. One of these additional language features happens to be class fields, however here is a complete list.

Mutable class properties

Note if the handleChange property from the above example were a method instead, in the form of:

handleChange() {
  /* handle change */
}

Then the would not work correctly because the callback is invoked in the global context instead of the context of the class component. What this means piratically is that this would refer to the global object which in case of the browser is the window instance, whose value can be anything. Alternatively, we could have bound the method in the constructor:

this.handleChange.bind(this)

Which was a common practice before the introduction arrow functions and the class field syntax.

“What’s the difference with arrow functions?” You may ask. Basically arrow functions do not have a dynamic context. Instead they “capture” the outer context in which they are declared in. Regardless of the context our handleChange function is invoked reference to this inside it’s body is going to refer to the component class context.

With that in mind, that above example is still not going to function as intended. It demonstrates the difference between state and refs. The console.log gives us a little more insight what’s happening. Namely, the event target’s value is assigned to the name field. In the beginning the name property is equal to “Jane”. Furthermore, if you enter the letter “a” with your cursor positioned at the end of the input, the handleChange method is invoked with an event whose target’s value is “Janea”. Next, if you enter the letter “e”, the handleChange method is again going to be invoked. The question is, what is the name field’s value going to be after the assignment? Pause for a second to think about it. I’ll list out a few options:

  1. Jane
  2. Janeae
  3. Janea
  4. Janee
So what's the correct option? It may still be a little confusing and that is normal, but if you analyze the event step by step , like we did before, everything becomes clear. The event target's value is going to be "Janee" because the components render method hasn't been invoked since (granted that the parent component hasn't been rerendered either). Having established this the rest is the same as before. Namely the value of the target (input) at the time of the keyboard event taking place is "Jane" and the character "e" which was entered is appended to the end. Finally, the name value "Janee" is assigned to the name field.

Now with that out of the way. We can use this property of refs to our advantage. In certain situations you may not want to have the component rerender, but you may still want to be able to react to the user’s input. In the following scenario we’ll keep track of the number of time the user clicked on the plus button until the count reaches 5, at which point the Count component is going to unmount and rely the information about the number of clicks to it’s parent

There’s one important detail to note here. Namely, in the Count component’s render method it is imperative not to destructure the count field. This is because, in JavaScript, numbers are among value types which. In contrast to reference types like objects, when a value type is assigned to a constant or variable, a new value is created and the constant or variable which is being assigned to becomes a reference to the newly created object. In this case, since we want to mutate the class property, hence we may to access the reference to the variable through the this object.

Accessing DOM node references

Bear in mind the example we are about to see can be improved upon and we will do so in a subsequent section.

In the example above we used the getBindingClientRect method on the HTMLDivElement instance to get the element’s width and height, in pixel units, at moment in time. Next, we set the component state causing the render method to be invoked once again, this time with the updated width and height values.

Accessing a class component instance

The ref prop is a special prop in the sense that you cannot access it inside a component by typing this.props.ref in case of class components. Although, if you pass a ref to a class component’s instance you’ll have access the instance object. Meaning that you will be able to imperatively invoke methods on the component and access public properties.

When would this be useful?

A prime example is setting the focus on an input element. Consider the text editor library draft-js. From my experience, the library has a hockey stick like learning curve. Meaning that it is difficult to learn at first, but once you grasp the basic concepts and mechanisms it becomes rather easy to do more complicated stuff with it. To quote the official draft-js docs

Managing text input focus can be a tricky task within React components. The browser focus/blur API is imperative, so setting or removing focus via declarative means purely through render() tends to feel awkward and incorrect, and it requires challenging attempts at controlling focus state.
With that in mind, at Facebook we often choose to expose focus() methods on components that wrap text inputs. This breaks the declarative paradigm, but it also simplifies the work needed for engineers to successfully manage focus behavior within their apps.

The editor component is basically a textarea, except that it isn’t a text area but instead relies on contentEditable which brings with itself a whole class of issues like cross browser inconsistencies. If you’re interested in this topic you can check out video by Isaac from the draft team. Luckily for us, the draft team has overcome these obstacles and presented draft-js users with a nice API.

createRef

In the section Accessing DOM node references I told you that the example can be improved upon and the time has come to see how.

That’s it? Doesn’t seem like much of an improvement

Indeed, it’s not a quantum leap. However, we didn’t have to explicitly type the editorRef property. Instead we passed the type of object whose reference we’d like to store.

Why did you pass EditorState | null, instead of just EditorState

Thanks for the question, this ties in with the constructor which too is newly added. Inside the body of the constructor method the value of this.editorRef is logged. At which point the ref is equal to { current: null }. We already mentioned that at the time of componentDidMount being invoked, refs will already have been updated. This means that the refs are updated somewhere in between the constructor phase and the before the mount phase.

The life cycle doesn’t finished there though. In the above example there’s also an additional componentWillUnmount method along with the App component which renders a button, which when clicked mounts or unmounts the MyEditor component. Pay attention to an extra difference between manually creating a ref object compared to using React.createRef. Namely, React is going to take care of setting the ref object, returned by invoking React.createRef, to back to { current: null } again.

Why is this a good thing?

Simply, the DOM node whose reference is stored in the class property does not exist in the DOM anymore after the component has been unmounted. React plays the role of your friend telling you that the coupon for the Indian restaurant which you were planning to spend is no longer valid because the restaurant already closed down. So going to the restaurant would be a waste.

In conclusion, when using the React.createRef API to access a DOM node’s reference. Initially the ref object’s current property is going to be equal to null. After the constructor phase and before componentDidMount is invoked, the ref object’s current property is going to be a reference to the given DOM node. Finally, before componentWillUnMount is invoked the ref object’s current property is once again going to equal to null.

Refs and functional components

Functional components do not support refs out of the box, since they are stateless and not produce object instances like classes. We can however forward a ref from a functional component.

The first type parameter of forwardRef is the type of HTML element the ref is going to be forwarded to. Whereas the second one represents the props of the component. Furthermore, the callback passed to the forwardRef function now received an additional second argument which is the ref to be forwarded. To which element the ref is going to be forwarded exactly is up to the functional component to decide.

Furthermore, if you’ve ever searched the web for a React component library chances are that you’ve come across material UI at some point. According to the following npm trends chart material ui is the most popular React component library. Popularity aside, material ui is a set of react components which implement the material design spec. If you’ve used any of Google’s products chances are that you’ve got a taste of the material design look and feel. Apart from that material ui has a wide assortment of component you can choose from which is ever evolving through @material-ui. All of the components are well documented with lots of examples and there’s even a theming system and a Box component primitive. There’s also a few quirks here and there as demonstrated above.

As a side note, I haven’t been payed to promote them, I laid out the major benefits of the library so that some of you may resonate with them.

useRef

There was a lot to take in when in comes to refs however if you’ve come this far you may indulge in the ergonomics of the useRef hook.

Once again, the code is quite a bit more concise. However, the editorRef declaration looks eerily similar to the above example where we used createRef.

Then, what’s the difference between between useRef and createRef? What would happen if we replaced the useRef call above with an createRef invocation?

You wouldn’t immediately notice a difference, however, it’s not recommended to use createRef inside the body of a functional component because of performance reasons. That is because createRef returns a new reference each time it is invoked, in this case being every time the FC is rendered. In the context of a single component life cycle, useRef, is always going to return the same reference.

What a build up and culmination, but I feel like something is missing…

Up until now, we talked about how refs can represent DOM node instances or class component instances. However we also talked about how refs can be basically anything. Furthermore, being able to access public properties on class component instance and imperatively invoke the methods seems like quite an advantage. This is, after all, a very common pattern in frameworks like Angular. This section is dedicated to the ones who asked themselves how this mechanism could be applied to functional components.

It might not seems perfectly clear how to go forward, but for starters, we know how to forward a ref to a functional component. In a sense, a functional component is equivalent to class component instance’s render method. As such, it may be difficult to imagine how a FC can harbor properties and method. The answer is simple. Constants and variables declared inside the function body may represent properties and functions may represent method.

But, what about a shared context, through this?

Guess what, the body of a functional component represents already context which is shared across variables and functions.

Taking this idea a step further, in contrary to class components everything inside a FC’s body is private by default. Meaning that the outside world has no knowledge of any variables or functions declared inside it. However, we do want to expose certain variables and functions. and we can easily do so by mutating the ref’s current property.

It works exactly like you’d expect it to. However, there are a few things to note here. Starting with the if (ref) inside the Hello component. It seems excessive according to the type signature of the ref which is ((instance: IHello) => void) | React.MutableRefObject<IHello>. There is no mentioned of null or undefined, however if you do not pass a ref to the component upon instantiation, like <Hello />, then the ref will simply be null when you try to access it in the scope of the Hello component. Although, it’s not likely that the consumer of the Hello component is going to omit the ref prop it is still a very real possibility considering that the TypeScript compiler issues no warnings or errors if you instantiate the component without providing a ref. This detail may lead to a runtime error and if we don’t test how the Hello component renders, the error may split by development unnoticed.

Okay, but what’s up with the ((instance: IHello) => void) part?

To quote the React docs:

React also supports another way to set refs called “callback refs”, which gives more fine-grain control over when refs are set and unset.
Instead of passing a ref attribute created by createRef(), you pass a function. The function receives the React component instance or HTML DOM element as its argument, which can be stored and accessed elsewhere.

You may not need to use the option, but it’s nice to know that it exists. Although don’t shy away from pointing out TypeScript’s shortcomings, and the shortcomings of type declarations provided by library authors and developers, the main point is that TypeScript still enables us to have an insight as to how a symbol can be used based on it’s type signature. I am a very lazy person by nature and I don’t always like looking up documentation. Ideally, I like to find out how to a third part function by simply hovering over the symbol in VSCode. As a cherry on top, VSCode also has first class support for JSDoc, meaning that information which is difficult if not impossible to convey through types at the moment can be expressed in plain English with a comment. This way you can annotate both function, classes, object, interfaces and so on.

But, wait, even if I use JavaScript in VSCode I am able to look at the type signature of the ref.

That’s true, but under the hood VSCode is running a TypeScript server to be able to do any type checking.

To get back to the main branch of discussion. If the ref exists and ref.current is not of type “function”, then we can feel free to assign an object which has a clear property to ref.current.

On the other hand, each time the component renders the variables and functions which are declared inside it are redeclared. The assignment to ref.current also needlessly happens each time. And those if checks are not very nice to look at. Not to beat around the bush anymore, there is a dedicated hook, created by the React team, for exactly this purpose.

useImperativeHandle

It covers all of the edge cases which we mentioned before and it provides a nice outward facing API.

Importantly, being an imperative API, useImperativeHandle is not meant to be a tool which you rely on constantly because it’s not in line with React’s philosophy of functional, declarative programming. It’s not bad per se, however there is probably a more elegant and idiomatic way to perform a given task.

Lifecycle methods

Hooks are used inside functional components and we already established, from the pre-hooks era, that there is no such thing as a functional component instance because it has no life cycle. There is no difference between the first render of a functional component as it is first mounted in the DOM (before it was not present in the virtual DOM hierarchy nor in the real DOM) and the last render, where the component is removed from the DOM. However, if we want to put class components aside and only use functional components, to implement the state pattern and life cycle pattern there has to be a way for developers to track the life cycle of FC’s. At least the mounting, updating and unmounting phases. Hooks bridge that gap and one such essential hook is the useEffect hook.

A very important thing to note is that every hook is effectful and functional components which consume a hook are impure. A function which doesn’t perform a side effect, even if it has “use” in its name, is not a hook but a pure function.

The convention to prefix each hook with “use” was proposed by the React team, similarly how each higher order component was prefixed with “with”. Khm connect, shame on you react-redux. The goal of the naming convention is to communicate to developers that a function has some special properties. In the case of hooks, it is the impure nature as well as the ability to trigger rerender among others. This is especially important in the case of hooks compared to HOC’s, since higher order components were distinguishable by their type signature which is:

type HOC = <InputProps, OutputProps>(component: React.ComponentType<InputProps>) => React.ComponentType<Output>

Whereas, the type signature of hooks is more generic and less recognizable:

type Hook = (...args: []) => any

In fact, the above is a type signature of any function for that matter. Based on the type signature alone, any function may or may not be a hook. However if you prefix your custom hooks with “use” is an important gesture towards your future self and other developers who are going to be working with your code. Furthermore, the highlight of this series is the Creating your own hooks section, where we’ll learn how to build world class custom hooks.

Before we start going through all of the examples, let me ask you this.

What the functional programmer answer to any object oriented pattern? Of course, it's a function every time.

How does this tie in into life cycle methods?

Simply, the answer to any life cycle method in the context of hooks is useEffect.

To understand the useEffect hook in it’s entirety took me quite a bit of time. Apart from reading the through the documentation I experimented a lot with the hook and through many iterations I was able to say that I have a thorough grasp of the hook.

In this section, we are going to go through a lot of examples step by step and incrementally learn everything you need to know to become a useEffect ninja.

One more heads up before we start. Thinking in terms of utilizing the useEffect hook can be difficult at first since it requires a shift in perspective. However, do not beat yourself up about it. After enough practice you will build up a solid intuition.

componentDidMount

">useEffect on mount

This was pretty straight forward, with nothing out of the ordinary. Next up, we’ll see how the pattern translates to functional components and hooks.

useEffect will unmount

Couldn’t be easier, could it? I feel like a broken record, but once again the code is more simpler and more concise.

Now, as to what the important details in the above example are. As you probably noticed the useEffect callback is returning a function. This function being returned is usually aptly called the cleanup function and this is where you perform your your cleanup duties, like unsubscribing from a data source. React will invoke that function each time the useEffect callback is ran, expect for the first time (this gave me a lot of confusion on the past). There’s one more exception to the rule, specifically, the cleanup function is also invoked when the component is about to be unmounted.

componentDidUpdate

Now that you know what React does with the dependency array in relation to the callback function and the cleanup function this section should be a breeze.

In case that we want to perform a side effect like logging to new value of a prop to the console, each time the given prop changes, we can easily do so by utilizing the componentDidUpdate hook. React will invoke the callback each time any of the props change thus executing our code. This entails that the responsibility for comparing props or performing any other logic for that matter is completely up to us. You’ll soon see why this was brought up.

useEffect did update

The dependency management mechanism is inverted in this case. Meaning that the callback is not going to be invoked unless a dependency is stated explicitly. Additionally, we do not have to perform a comparison of the props anymore, however the downside is that we do not have access to the previous props either. Although there exists a simple workaround and we going to be implementing in a section to come.

Let’s first talk a bit more the dependencies. At this point, we know what the dependencies represent and how React manages the dependency array which we provide. Now, we are going to touch upon the why and the best practices around managing dependencies.

The why of dependencies

So, why does the dependency array even exist. If you think about it, the compiler could conclude what the dependencies of useEffect callback are based on the variables which we refer to in the callback. It is possible and the React documentation even states:

The array of dependencies is not passed as arguments to the effect function. Conceptually, though, that’s what they represent: every value referenced inside the effect function should also appear in the dependencies array. In the future, a sufficiently advanced compiler could create this array automatically.

This insert hints at the total removal of the dependency array. However, there would still be a need for a way to convey the information that a certain effect should be run only once. Otherwise, it would come down to an if statement inside the callback whose responsibility would be to determine whether based on the current state of the world. Something along the lines of:

React.useEffect(() => {
  if (status === 'initial') {
    setStatus('loading')
    fetchResource()
  }
})

useLayoutEffect

The following hook, called useLayoutEffect, is similar to useEffect. In some situations the 2 of them will be interchangeable. In which case the React team recommends using useEffect. What follows is a great tip from the official docs:

If you’re migrating code from a class component, note useLayoutEffect fires in the same phase as componentDidMount and componentDidUpdate. However, we recommend starting with useEffect first and only trying useLayoutEffect if that causes a problem.
If you use server rendering, keep in mind that neither useLayoutEffect nor useEffect can run until the JavaScript is downloaded. This is why React warns when a server-rendered component contains useLayoutEffect. To fix this, either move that logic to useEffect (if it isn’t necessary for the first render), or delay showing that component until after the client renders (if the HTML looks broken until useLayoutEffect runs).
To exclude a component that needs layout effects from the server-rendered HTML, render it conditionally with showChild && <Child /> and defer showing it with useEffect(() => { setShowChild(true); }, []). This way, the UI doesn’t appear broken before hydration.

It’s important to note that useLayout fire synchronously after all DOM mutations and gives you a chance to execute a callback at the cost of blocking rendering by preventing the browser from painting. So do not perform any blocking or heavily computational operations inside the hook if you want your component to be visible on the screen as soon as possible. Don’t even use the useLayoutEffect hook if you don’t have to.

So what’s the hook even good for, anyway?

You will know when the time comes to use it. For example, if your component needs to be aware of it’s size and position before it is rendered for the first time. If part B of the layout depends on the dimensions of part A of the layout and you want to avoid a flash of unstyled content.

Let’s see the example:

This might seem like a niche use case. Regardless, the React team has got us covered. This is one of those moments which instills confidence in React as a tool all encompassing tool for building web applications.

Lastly, you may already be familiar with the usage of React.Fragment, it’s a way to be able to return multiple elements at once. They are still wrapped in a React.Fragment however the fragment is stripped at build time and is not present in the DOM. You may also have seen the newer syntax for fragments which is more concise, <>/\* content \*/</>. Yes, they look like empty HTML tags. However the Stackblitz React TypeScript template does not support it at the moment and fails to parse the symbols. Fragments are a nice way to avoid having div soups. Nevertheless, like any other good thing, don’t overuse them. Believe me, I got burned once when I ended up with a broken layout. Long story short, by wrapping two sections in a fragment instead of a div. The 2 sections distributed evenly across the parent flex container, instead of splitting 1 fraction among themselves. Luckily for me, it was easily noticeable.

Properties and methods

We talked about how functional component, with the additional of hooks, stand toe to toe with class component in almost any field. However, up until now you may have noticed that something is not quite there. In other words, if we declare a constant or function inside a functional component it gets redeclared and recomputed each time the FC renders. The re-declaration entices referential inequality and re-computation spends additional resources. The downsides of these two factors is usually not detrimental, but there is a solution for this conundrum as well. The solution comes in the form of the useMemo hook and it’s specialized version called useCallback. Let’s first explore the elder of the two siblings.

useMemo

As its name suggests, the point of the useMemo hook is to enable memoization, preventing needless recalculation.

What does this mean exactly ?

As you might know, every pure function have certain neat properties. Starting with the fact that they always return the same output for a given input. They are just pure data transformations. Based on this, you could create a table of mapping certain inputs to certain outputs, for any input (given that the function is also total - for every input there’s a well defined output) and any pure function ever. useMemo takes advantage of this property and memoizes the callback. Under the hood, it creates a table of know input and output mapping. If at any point it receives a known input, the input is mapped to an output and returned without the need to invoke the callback.

So pay attention to the mentioned of pure part, even though the signature of useMemo looks similar to useEffect - side effect don’t belong in useMemo
callbacks. Effectful functions do not exhibit the same properties.

Without further ado, let’s see what the useMemo hook can offer us.

The first usage of useMemo is trivial. What’s more, the first argument passed to useMemo is a callback which when invoked return the ordinal which is the result of casting the text representation of the input’s value to a number using th Number constructor. It doesn’t save us much computational resources however it prevents the ordinal constant from being redeclared each time the App renders.

Is this really worth it?

That’s a good question. Since the usage of useMemo impacts readability, I’d rather not write use it everywhere unless there’s a fathomable benefit to using it. There are plenty of ways to measure the impact of the useMemo hook and soon we are going to see for ourselves. However, let’s first get acquitted with the little brother of useMemo.

useCallback

As its name says, useCallback is often used to wrap callbacks. And the docs say a little more about it:

Pass an inline callback and an array of dependencies. useCallback will return a memoized version of the callback that only changes if one of the dependencies has changed. This is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders (e.g. shouldComponentUpdate).

At its heart useCallback(fn, deps) is equivalent to useMemo(() => fn, deps).

Here’s an example which you may come across in the wild, debouncing an effectful function which depends on an input value.

We aren’t passing an inline lambda function to useCallback because the return type of higher order debounce function is a function. Higher order function means exactly, a function which also returns a function. This is similar to another familiar concept which we will be discussing called higher order components. Even if you weren’t sure before, applying the same logic you already know what higher order components are. Back to the debounce example, be careful not to debounce the value setter else the input will appear broken.

useMemo vs React.memo

It is worth mentioning that useMemo and React.memo, despite having a similar name, have many things in common apart from that. Let’s look at the signatures of each function, starting with useMemo:

type useMemo = <Value>(callback: () => Value, dependencies: any[]) => void

Compared to React.memo:

import React from 'react'
type memo = <Props extends {}>(component: (props: Props) => React.ReactNode, arePropsEqual: (prevProps, nextProps) => boolean) => void

Firstly, memo doesn’t accept a dependencies array, but instead accept a function as the second argument. When the function returns true, it signals to React that the props are equal to the previous props by the criteria you establish, and a rerender is unnecessary. Although, the comparison function represent an optional argument, and if it’s not provided a shallow comparison is performed by React.

And also noticeably, the callback param of useCallback doesn’t any parameters.Let’s now spice things up by use rxjs to subscribe to a service which return a new value every second. With subscriptions you’re probably used to unsubscribing in the unmounting phase, from which point on the component instance no longer exists and keeping the subscription running would be a memory leak. This is even suggested by React’s error message during development, when there’s an attempt to update the state of a component which has been unmounted.

Is memoization worth it

From my very first contact with the memoization hooks (useMemo and useCallback), I liked the idea of being able to further optimize my code’s performance. However as we discussed, the usage of the before mentioned hooks comes at the cost of readability. So in order to be able to make quality decisions on whether or not the trade-off is worth it in a given situation, it is essential to know the impact and have proof concrete backing up your decisions. Fortunately, React provides us with a Swiss army knife of tools one of which is the Profiler component. The Profiler is very powerful and yet very simple to use.

The majority of the complexity related to the Profiler comes down to the onRender callback. It accepts a number of arguments:

prop description
id the “id” prop of the Profiler tree that has just committed
phase either “mount” (if the tree just mounted) or “update” (if it re-rendered)
actualDuration time spent rendering the committed update
baseDuration estimated time to render the entire subtree without memoization
startTime when React began rendering this update
commitTime when React committed this update
interactions the Set of interactions belonging to this update

We are going to be focusing on the properties which enable us to determine the effects of useMemo. Firstly, the id is required and its of type string. The string should match the name of the component you rare profiling. It is also required for the component being profiled appear as a child of the respective Profiler. Secondly, the actualDuration. It is going to be the limelight of the experiments. The reason for it is encompassed by the following insert from the docs:

actualDuration: number - Time spent rendering the Profiler and its descendants for the current update. This indicates how well the subtree makes use of memoization (e.g. React.memo, useMemo, shouldComponentUpdate). Ideally this value should decrease significantly after the initial mount as many of the descendants will only need to re-render if their specific props change.

This is the setup and experiment which will be performed illustrates the point but is rather simple. Namely, the getFibonacci function is going to enable us to incrementally increase the computational strain on the non-memoized component which will sooner or later stand out compared to the memoized version. What’s more, the actualDuration of the render for the memoized component should stay roughly the same in the case that we are not updating any dependency of the getFibonacci function (the ordinal to be exact). The getFibonacci function’s time complexity increases drastically as we increase the ordinal, to the point that after having entered 9999 I received a stackoverflow error(this was performed on a second generation Ryzen 7 laptop). Since there is a significant jump in complexity with each additional digit the idea is to run benchmarks for a single, double and triple digits. Furthermore, to be able to compare the results we are going to render a non-memoized and a memoized version of the Fibonacci component. By non-memoized I mean a component which calculates the Fibonacci number at a given place based on the supplied ordinal prop. Each time the component renders the computation is going to be performed form the ground up. Whereas the memoized sibling of the component has the computation wrapped in a useMemo hook with a single dependency, the ordinal, which is passed as an argument to the getFibonacci function. On the other hand, we are not going to be running the tests for both component simultaneously since they rely on the same state. Although, every step along the way, including the most insightful growth rate statistic are going to be logged to the console. In this example I specified 50 iterations and based on the profiler data from those 50 iterations an average is induced. Moreover, based on the averages the growth rates are calculated from lower to higher (left to right).

After a couple of runs, in case of the memoized component the growth rates where deviations I observed where around 20%. Although the averages, in case of the memoized component, are expected to be roughly the same, there are divinations and that is normal considering that the actualDuration is a small number. Meaning that the absolute differences are miniscule. On the other hand after having ran the non-memoized version of the component a number of times some distinctions where obvious. Namely, the growth rate from the 9th to the 99th was circa 20% on average. Although as mentioned before this isn’t a significant amount, however the increase was rather consistent. Moreover, the growth rate from the 99th to the 999th shot up by a more significant margin. Around 300% on average, now that is a more noticeable result.

The conclusion is that’s probably not necessary to memoize simple expressions and functions unless you are working on a piece of code in the critical path or you require referential integrity to be preserved when working with highly optimized components.

Rules of hooks

I shall share with you a summary the dedicated section in the official docs, called Rules of Hooks. Both principles which we are going to discuss revolve around the same requirement and that is to not change the order in which the hooks are rendered at any point of the components life cycle. Else undefined behavior is likely to happen. If you’re interested to find out why, here’s a great example. The first principle says:

Don’t call Hooks inside loops, conditions, or nested functions. Instead, always use Hooks at the top level of your React function. By following this rule, you ensure that Hooks are called in the same order each time a component renders. That’s what allows React to correctly preserve the state of Hooks between multiple useState and useEffect calls.

It is important to remember that the principles are there to protect you. They may even overprotect you, but up to a point it’s still better than being under-protecting. An example of an overprotective principle is:

Don’t call Hooks inside nested functions

The point is that it’s imperative to respect the requirement that hooks are to be called in the same order, from render A to render Z. Invoking hooks inside nested functions does not jeopardizing the requirement, unless you use a loop or a conditional inside it. Heck, even using a loop or conditional statement without breaking the contract. However, in the case of a conditional like an if statement, a hook may not get called during a given render causing other hooks to be rendered out of order. As with loops, like forEach, the length of the array may vary, which entails the same negative effects pertaining to conditionals. To demonstrate the benevolence of hooks inside nested functions I created the following stress test:

Feel free to experiment yourself.

Then, why did the React team declare hooks inside nested functions as an anti-pattern?

It’s easier to check whether a given piece of code, which uses hooks, is doing something which may upset the hook invocation order if you forbid hooks inside nested functions entirely. On this occasion, the React team was probably cornered and had to make a decision. They chose the safer path, which is understandable. Taking into account React’s widespread adoption and the fact that bugs introduced by not following the consistent hook order requirement are difficult to debug and reproduce, it’s safer to have false positives than undetected issues.

Based on the research, it goes without saying, that using hooks in async functions are part of the same, code red, category as loops and conditionals. Because there’s a high chance that the order in which hooks are invoked is going to change from one render to another.

And the second principle states:

Don’t call Hooks from regular JavaScript functions. Instead, you can:

  • ✅ Call Hooks from React function components.
  • ✅ Call Hooks from custom Hooks (we’ll learn about them on the next page).

To clarify the point:

Don’t call Hooks from regular JavaScript functions

If a JavaScript function invokes a hooks inside its body. Firstly, you may consider prefixing its name with “use”, if that is not already the case. Secondly, and more importantly, the rules of hooks now apply to the function which consumes another hook. Furthermore, if you use the newly created custom hook inside another function, it too becomes a hook inheriting the naming convention and rules. And so on, and so forth.

As for remembering the rules, luckily for us lazy developers who are not fond of learning all the things by heart. Khm, like we had to do at school. The React team created an eslint plugin which encompasses the above mentioned rules and basically teaches your IDE how to point out when you are doing something borderline. Just to see if the useState hook inside a callback is something that you did on purpose or a prank. Unfortunately, the IDE doesn’t much of a sense of humor.

Finally, remember how we talked about rules of hooks being overprotective at time… In occasions where the order of hooks is guaranteed between renders and eslint still raises an error or a warning, you may disable the specific rule for the line which is the locus. For arguments sake let’s that you want to disable a complaint about a hook inside a nested function, in which case you’d target the react-hooks/rules-of-hooks rule:

// eslint-disable-line react-hooks/rules-of-hooks
or
// eslint-disable-next-line react-hooks/rules-of-hooks

One more example where the hooks eslint plugin is being overprotective, which I commonly come across, is the react-hooks/exhaustive-deps rule. So what’s the eslint rule about. It is pretty straightforward:

import React from 'react'

const Hello: React.FC<{ name, fetchName: () => void }> = ({ name, fetchName }) => {
  React.useEffect(() => {
    fetchName()
  }, [])
  return <div>{name}</div>
}

Similarly to the previous example it is a case of a rule sanity check rule. With the assumption that it is more likely that you forgot to declare a dependency, in which case your effect is not going to run as often as it may should, depending on the use case. Compared to the scenario where you purposefully didn’t declare a dependency because you want a certain function to be invoked only once, when the component has been mounted. Same as before, you may suppress the warning like so:

import React from 'react'

const Hello: React.FC<{ name, fetchName: () => void }> = ({ name, fetchName }) => {
  React.useEffect(() => {
    fetchName()
  }, []) // eslint-disable-line react-hooks/exhaustive-deps
  return <div>{name}</div>
}

useDebugValue

The last among the hooks, is the useDebugValue hook. I only recently found out about the hook, blessed be intellisense, and my reaction was along the lines of:

“Wait there’s a hook called useDebugValue, that sounds intriguing, so what does it do?”

useDebugValue can be used to display a label for custom hooks in React DevTools.

“I love React DevTools, this must a really great addition to my hooks arsenal! Let’s test it out. Okay, I’ve created a custom hook. It’s named useValue, so its label in DevTools should be equivalent to Value.”

“It is, alright! That wasn’t difficult.”

“Just a second. This means that the default label is useValue, let me test it.”

*Comments out useDebug invocation*

“Hmmm, the DevTools still show Value. It must be an issue with hot reloading. Let me refresh the page. It’s still the same… Let me make sure that I correctly saved the file. It’s still the same. And what about the commented out hook - nothings out of the ordinary.”

So I can to the conclusion that with version 4.6.0 of React DevTools it is no longer necessary to use the useDebugValue hook since the name of the hook will be properly formatted and used as the label. However, there still may be a use case for the useDebugValue hook and it has to do with the second parameter the hook accepts. There are no mentions of this parameter in the docs, luckily for use the typings for react provides more insight.

What’s the point of the second parameter?

It’s simple - it’s named format and it does exactly that. it’s signature depends on the first argument which is the value returned by the hook, meaning that if the value were a string the type signature of the format function would be format: (value: string) => any. Albeit useful, the ability to format the value is not something which you would use consistently. What’s more, in the case of the useValue hook applying any formatting like upper-casing or lower-casing the value would just be confusing. Nevertheless, the formatting may play a bigger role when the return type of the hook is a complex object.

Enough storytelling, let’s get our beaks wet by writing our first custom hook! Event though the useDebugValue hook is less exciting, writing custom hooks is a thrill.

Notice the usage of the useCallback hook, when writing hooks, which are reusable, especially as a library author, you may want to pay special attention to writing efficient code in every sense of the word. Make the consumers of your custom hook look up to you in quality. Importantly, however, nobody writes perfect code from the get go, unless they have written a similar program before, furthermore a custom hook is useless if it doesn’t work correctly. Hence, make sure that your custom hooks work before refactoring your code to increase it’s quality.

A word which often goes hand to hand with the word refactoring is the word TDD and writing test. Just as it is difficult to write high quality efficient code in a single iteration, it is also difficult to refactor your code without first having written the the tests for it. Ask uncle Bob if you don’t believe me. No, seriously check out one of his videos on the topic of TDD.

Now that I’ve talked about writing custom hooks and writing test in the same sentence, I’ve probably left you wondering how to test custom hooks and some of you may also wonder how to test your components which consume React hooks or custom hooks. I didn’t plan to include this section in the course but I’ll make sure to leave room for it. So be on the lookout in of of the future section.

Creating your own hooks

Now that we have mastered the basics we are able to move onto more advanced topics. Imagine yourself as the painter who has acquired all the necessary colors on his or her palette and is ready to paint a masterpiece.

Implementing redux with useContext and useReducer

Are you used to having a global immutable data store which serves as your app single source of truth?
I am too! So let’s implement a basic version of redux using just 2 hooks that we mentioned before.

useFetch

useState and useEffect

usePagination

It’s getting ever more exiting! In the next section we are going to built upon the example from the previous section, however not only that. We are also going to take the idea one step further by implementing pagination.

useState and useEffect and IntersectionObserver API

Check out react-query and/or swr and react-window and/or react-virtualized

Going up a notch with middleware

Now we are shifting into the fifth gear when.

If you haven’t done so already check out redux-thunk and redux-observable

You may have noticed that this approach conflicts with relying on a hook similar to useFetch. I’ve been a proponent of using redux-observable for long time now, because performing side effects like network requests in epics, using rxjs gives you a lot of freedom. You are able to denounce, throttle, cancel requests and more.

However, more recently, I’ve been looking into relying on libraries like react-query to handle “server state” related matters. There are many carefully though out mechanisms which are at your services, out of the box, when using both react-query and swr. Let’s go over the major concepts, which although possible to implement solely using redux-observable and rxjs, require quite a bit of additional effort.

However, I’ve considered creating a library which would bridge that gap for redux-observable. If you are aware of something similar which already fulfills such a role, pray let me know.

Angular like services and dependency injection

Imagine that you are building an app which is using JWT based authentication. The JWT has to be stored on the client after the user logs in. However, we cannot store the token as part of the global state or any component state for that matter because both concepts are tied to individual sessions. Meaning that once the user closes the tab in which your app once resided, the information about the token is gone. In case that the token has not yet expired, it would be inefficient to ask the user to log in once they return to the app. We can provide a better user experience by storing the token in a place which persists through sessions and let the server decide when a token has expired and it’s time to reauthenticate. Luckily for us there are many options to choose from which fit this description however a popular option is to use the localStorage API.

useState and useEffect

Testing custom hooks

TODO

Testing components which use custom hooks

TODO

Hooks vs HOC’s

You can read about what the creator of recompose, the de facto React HOC’s library, had say about hooks at the top of the repository’s README file. However, in a nutshell, React is heading towards hooks and recompose is no longer actively maintained.

Pros of hooks

  • Easier and faster to set up. Take the example of selecting a value from the redux store:

Selecting a value from the state with HOC’s

import React from 'react'
import { connect } from 'react-redux'
import { State, selectName } from '../store'

const Hello: React.FC<{ name: string }> = ({ name }) => (
  <div>Hello {name}</div>
)

export const HelloContainer = connect((state: State) => ({
  name: selectName(state)
}))(Hello)

Selecting a value from state with hooks

import React from 'react'
import { useSelector } from 'react-redux'
import { State, selectName } from '../store'

const Hello: React.FC = ({ name }) => {
  const name = useSelector(selectName)
  return (
    <div>Hello {name}</div>
  )
}

Cons of hooks

  • Hooks are more difficult to reuse if you want to keep a clear separation between container and presentation components. Let’s revisit the previous example

Create multiple components with a single HOC

import React from 'react'
import { connect } from 'react-redux'
import { State, selectName } from '../store'
import { Hello } from './Hello'
import { Hello1 } from './Hello1'

const connectWithName = connect((state: State) => ({
  name: selectName(state)
}))

export const HelloContainer = connectWithName(Hello)

export const Hello1Container = connectWithName(Hello1)

Notice that we didn’t even import React in the example above. Because there is no need to, since importing React is only required your file contains JSX or TSX. What’s more, the file extension may be shortened from tsx to ts.

Create multiple components with a single hook

import React from 'react'
import { connect } from 'react-redux'
import { State, selectName } from '../store'
import { Hello } from './Hello'
import { Hello1 } from './Hello1'

export const HelloContainer = () => {
  const name = useSelector(selectName)
  return <Hello name={name} />
}

export const Hello1Container = () => {
  const name = useSelector(selectName)
  return <Hello1 />
}

Code-wise there’s not much of a difference, however if we take a more holistic approach the point becomes more obvious. Not seldom, will you want inject a certain prop or props and spread the rest.

Create multiple components with a single HOC 2

import { connect } from 'react-redux'
import { State, selectName } from '../store'
import { Dashboard } from './Dashboard'
import { Sidebar } from './Sidebar'

const connectWithName = connect((state: State) => ({
  name: selectName(state)
}))

export const DashboardContainer = connectWithName(Dashboard)

export const SidebarContainer = connectWithName(Sidebar)

The above example has the exact same structure as before.

Create multiple components with a single hook 2

import React from 'react'
import { connect } from 'react-redux'
import { State, selectName } from '../store'
import { Dashboard } from './Dashboard'
import { Sidebar } from './Sidebar'

export const DashboardContainer = (props: Omit<React.ComponentProps<typeof Dashboard>, 'name'>) => {
  const name = useSelector(selectName)
  return <Dashboard {...props} name={name} />
}

export const SidebarContainer = (props: Omit<React.ComponentProps<typeof Sidebar>, 'name'>) => {
  const name = useSelector(selectName)
  return <Sidebar {...props} name={name} />
}

Now the difference is more obvious. There’s some boilerplate which is repeated from one case to another. Nevertheless, we already know how to easily abstract away such boilerplate code. Yes, we can create a higher order component which utilizes a hooks or hooks inside it’s body. In the case of react-redux you may simply reach for the connect HOC. What’s more, creating a HOC which utilizes a custom hook is trivial.

import React from 'react'
import { Sidebar } from './Sidebar'

export const withModal = <Props extends {}>(Component: React.ComponentType<Props>) => {
  const ComponentWithModal = (props: Omit<React.ComponentProps<typeof Dashboard>, 'isModalOpen' | 'toggleOpenModal'>) => {
    const modal = React.useContext(ModalContext)
    return <Component {...props} isModalOpen={modal.isOpen} toggleOpenModal={modal.toggleOpen} />
  }
  ComponentWithModal.displayName = `withModal(${Component.displayName || Component.name})`
  return ComponentWithModal
}

export const SidebarContainer = withModal(Sidebar)

The part where we a assign a displayName to the new component is optional, However, it aids debugging through the usage of React DevTools

Observing things from this perspective, HOC’s and hooks can coexist with each other in a state of harmony.

But, why bother writing pure components?

First of all, you can easily instantiate the component with randomly generated props with the help of a library like faker.

Moreover, many benefits are derived from the above fact.

Firstly your component is easy to unit test using react testing library or enzyme.

Secondly, you can easily reuse a pure component across projects by publishing it using a tool like bit

Thirdly, you may effortless develop and display your components in storybook or a similar environment etc.

As a side note, you can still test an impure component which consumes a hook, without actually performing any side effects like network calls by mocking the function. For example, using jest.

Before we cover the next section, which is really important, let’s first glance over the following terms.

Serializable - A message is serializeable if it can be stringified to JSON - which is not possible in the case of recursive structures.

CQRS - Command Query Responsibility Segregation, states that every method should either be a command that performs an action, or a query that returns data to the caller, but not both.

There is, however, a third approach inspired by microservices. Namely, if you think redux as a message broker and your components as services. In a nutshell, the main selling point of a microservice architecture is that it enables you to keep your services decoupled. For example service A, which depends on service B, doesn’t have to know how to communicate with service B directly. Else if service B were to be replaced with service C which behaves completely the same as B, despite the fact, service A would have to be altered. Namely, the reference which A holds to B would have to be replaced with a reference to C. A message broker solves this issue by having the individual service dispatch messages which are usually serializeable. The message broker is now responsible for passing the messages between services and the services themselves just have to know which actions they want to listen to and dispatch. You may have noticed that we haven’t entirely decoupled our services from the rest of the world (else the service would turn into a pure function), we just narrowed down it’s dependencies to a minimum. The only reference the services have to hold is a reference to the message broker. Specifically how to read and write (select and dispatch actions). Following the principles of CQRS it is preferable to keep these two operations separate. Fortunately, the message broker is not something which is often replaced. Even if it did, due to the fundamental simplicity of a message broker, the upgrade would be large scale but not very complicated. This means that it could be automated.

In conclusion, hooks are a great addition to the react ecosystem and there are plenty of use cases for hooks. Similarly to HOC’s hooks may encapsulate logic and/or side effects and keep out code DRY. On top of that, hooks can be used inside the body of functional component. Taking that into consideration, never again do you have to write a class component. Currently, however, with the notable exception of error boundaries. For more information about this limitation, check out why is X not a hook?.

Migrating common libraries to hooks

All of the major react libraries, which have previously relied on HOC’s, have migrated or are in the of migrating to hooks.

But does this mean that I have to relearn every single one of them.

The short answer is no. There may be some differences here and there, but having come this far into the course, you are absolutely equipped for tackling such migrations. Nevertheless, in this section we are going to cover some of the most prevalent cases and what better way is there to kick off this section than to talk about the react integration of redux. Give it up for react-redux.

react-redux

I bet that you’ll rarely ever want to connect another component using the higher order component API after you’ve got a taste for the the hooks react-redux has to offer.

connect

TODO

useSelector

TODO

useDispatch

Reading this section left a bad aftertaste and if you’re like me, the immediate though that crossed my mind was: “Lemme implement the useActions hook.” I’ll let you in on a secret. Once I implemented the useActions I never looked back. So as an added bonus I’ll show you to implement the useActions in a type safe manner.

useActions

TODO

react-router

withRouter

TODO

useHistory

TODO

useParams

TODO

useRouter

TODO

material-ui

Material UI is using jss under the hood and exposes a similar API compared to react-jss hence if you learn one you have learned the other the a significant extent safe for certain caveats.

makeStyle

This is too easy.

I agree. So for the next section let’s do something more difficult by implementing a prop based animation. In order to make this work we are going to overcome a few hurdles along the way, but have faith.

xstate

TODO

styled-components

TODO

react-spring

TODO

Parting message

Continue to learn and explore. Don’t be afraid to dig into documentation, but never stop practicing. And the sky is the limit for you.