This article assumes a basic understanding of the React APIs such as Context and Render Props. Do not worry if you do not understand some of the code examples or terms used in this article.
React has been around for more than 5 years now, and it has its pros and cons. A major pro is that developers using React will never get bored. It is constantly evolving to make building great user interfaces easier. The React Hooks API is one feature proposed to make React components cleaner, more maintainable and reusable. This article aims to improve your understanding of this API, and the problems it tries to solve.
So, What Are React Hooks?
React Hooks are functions that provide access to React features you already know, in a way that really explains and collocates the logic within a component. Hooks allow developers to separate the code based on what it is doing, rather than the component’s lifecycle. Let’s look at a few examples to understand what that really means.
Let’s say we have a Component DisplayWindowWidth that displays the current window width. Here’s how we would currently go about implementing this Component (please note I will be using the class properties proposal included with CRA babel-plugin-proposal-class-properties for these examples):
Despite such a simple logic, we had to use a Class based Component (which in itself can be confusing for beginners) and use the lifecycle methods. We have to explicitly tell React when we want the event listener to be added and removed using the appropriate lifecycle methods. As the Component logic gets more and more complex, it becomes pretty hard to see the code and identify whether or not we have cleaned up the resources we were using, or used the right lifecycle method for the right purpose.
Now, let’s see how the new React Hooks API can help us achieve the same functionality.
The Component is now a simple functional component (even though it has state) and much more concise. Let’s address the two unfamiliar functions in the above code – useState & useEffect. These two functions are, in fact, two of the built-in Hooks that React provides. A full list of the available React Hooks can be found here.
useState is a function that React has provided to make a functional Component stateful. We initialise the state by passing in an initial value to the useState hook and use array de-structuring to obtain the current state and the function to mutate this state. Since these are now just function variables, we can give any name to these variables. We can also use multiple useState calls inside a Component and each call gets its isolated local state inside the Component.
Similarly, useEffect is also a function used to perform the operations that we would usually perform in lifecycle methods such as ComponentDidMount(), ComponentDidUpdate(), ComponentWillUnmount() etc. The difference is, the logic is now collocated and we have delegated the responsibility of handling creating, updating and cleaning up of resources to React. useEffect accepts a function that can be used to perform mutations, subscription and other side effects. We can also choose to return a tear down function from this that will be used for clean up, similar to what we would otherwise do inside ComponentWillUnmount lifecycle method. With the current code, the effect will run for every render. However, we can tell useEffect to perform updates only when a particular state or props field changes – or in other words perform diffing on state fields or props, we can do something like this in the below code (notice the second argument).
Now that we have encapsulated the Component logic in functions, we can do some more cleanup. We can, in fact, go ahead and pull out the entire logic to handle the state and effect into a custom Hook as shown in the following code snippet. By doing so, we have now made the logic reusable across Components. We can safely do so because (as already mentioned above) each useState call gets its isolated local state inside the Component.
So far so good. Now, let’s look at another React feature that we can use in a Component – the Context. Don’t worry if you are not familiar with Context in React, just think of it as global variables for a Component subtree. So, say we want to apply a particular theme and a locale to a Component subtree, we can do that using the Render Prop API like so:
The example above shows a very simple Component but it manages to highlight another issue with the current API – we had to restructure our Component to leverage the Render Props API and by doing so, we have created a hierarchy that is not driven by the UI, but by the need to share data within the Component tree. As the underlying Component tree grows and/or more nested Contexts are being added, it becomes increasingly hard to understand the data flow within the Component tree.
Let’s now see how we can achieve the same functionality using one of the built-in Hooks from the new API and if you have been paying attention, you may have already guessed how we can do this (hint: useContext).
The Component as a whole starts to look much less intimidating. Not only did we get rid of the False Hierarchies, we also managed to make the code more readable and easy to understand. Additionally, useContext not only reads the value but also subscribes to the Context.
The fun with Hooks does not just end here. If you’re familiar with Redux and prefer to keep state changes predictable and declarative, you can ditch the useState Hook and use (you guessed it) useReducer hook as demonstrated here and below:
The API provides a few more useful Hooks which we have not covered in this article.
Since Hooks are just functions, we can easily build custom Hooks composed of one or more built-in Hooks and reuse them across Components. However, there are certain restrictions or rules to use React Hooks – the two most important ones are:
- Hooks should not be called inside loops, conditions or nested functions (much like state in Class Components).
- Hooks should not be called from regular JavaScript functions. They should only be called from a React function component or from custom Hooks.
Both these rules are enforced by a ESLint plugin eslint-plugin-react-hooks.
React Hooks might seem a bit weird and confusing at first, and that is to be expected. I believe it will take some time for developers to widely adopt it. Having said that, it is also worth noting what Dan Abramov mentioned in his React conf 2018 talk –
“Hooks represent the core team’s vision for the future of React”
So, Hooks can very well become the standard, and widely accepted, way of doing things the “React way” in the near future.
At Media Suite, we have not yet used this API for any of our partner projects. While this API looks exciting and we look forward to see how this is adopted by the wider community of React developers, we also believe that the benefits of using a stable API outweighs the benefits of using cutting edge or experimental technologies. Our focus is on working with our partners to get to the bottom of, and resolve their problems. Delivering cost-effective and efficient solutions is a big part of this, and we can achieve it best by leveraging established best-practices. We’re excited to see the Hooks API in action, and it is very likely to make its way into future React developments at Media Suite,
If you want to play around and experiment, React Hooks have been released as part of version 16.8 and the original proposal can be found here. The React conference 2018 presentation introducing React Hooks by Sophie Alpert and Dan Abramov can be found here. I encourage everyone to play around and have fun with this new API!
Subhra completed his undergraduate degree in Electronics in India, before coming to New Zealand to pursue his true passion for software development. He completed a Graduate Diploma in Science (specialising in Computer Science) in 2018 at the University of Canterbury. While interning at Media Suite, he worked on a complex software project within one of our core development teams, working in React and Django. He wrote this blog as part of his internship, but (cos we like him so dang much) has recently continued to work for Media Suite in a contract capacity.