React 16.8

Photo by Fabien Bazanegue on Unsplash

The last October 25th Sophie Alpert and Dan Abramov introduced the new React proposal version at the React conf 2018:

React v16.8 is meant for solving some problems that we’ve been dealing with for so long:

  • Confusing classes for both human and machines: Classes require a lot of boilerplate, force us to bind event handlers and to understand how this works in Javascript, present issues for some tools like hot reloading, decreases the compilation performance, etc.
  • Reusing logic: React doesn’t offer a way to “attach” reusable behavior to a component. Mixins was the first workaround but we ended up concluding that mixins comes with worse problems that the one we were trying to solve. Patterns like render props and HOCs solve this problem but they force us to restructure our components by surrounding them with several abstractions, which leads to the “wrapper hell”.
  • Giant components: The more a component grows, the more we have to spread its logic and side effects through the whole component itself. This happens mainly for two reasons: lifecycle methods containing a mix of unrelated logic and the impossibility to split these components into smaller ones because the stateful logic cannot be extracted.

The thing is that if we approach each problem in an isolated manner, it seems like solving one of them makes some other worst. For example trying to extract logic from a giant component leads to the wrapper hell and viceversa.

From now, we can consider the three problems described above not as isolated problems but as symptoms of the same problem: React doesn’t provide a stateful primitive simpler than a class component.

Hooks

Hooks are the new feature proposal that solves the problem:

  • Reusing logic: With Hooks, you can extract stateful logic from a component and reuse it without changing your component hierarchy.
  • Giant components: Hooks let you split one component into smaller functions based on what pieces are logically related, rather than forcing a split based on lifecycle methods.
  • Confusing classes: Hooks let you use React’s features without classes.

Hooks are functions that let you “hook into” React state and lifecycle features from functional components. Actually hooks only work in functional components, not in class components nor in plain javascript functions except from your custom hooks (we will talk a little about this later).

Hooks are also completely opt-in and 100% backwards-compatible so we can migrate our projects and start using them seamlessly! yay!

The State Hook: useState

Below there is a simple counter implemented without using hooks:

And below is the same component but refactored to use the useState hook:

So the state hook allows us to declare a “state variable” which will be preserved between renders by React. useState accepts only one argument, the initial value (value for the state variable in the initial render), and returns a pair of values: the current value and the updater function.

How does the updater function setState work? Much the same as the “old one”, but the new setState will replace the whole state with the argument passed instead of merging it with the old state.

So how would we store our state objects? We can still do the merge manually in the updater function, but it won’t be necessary in most cases since we can use as many state variables as we want in the same component:

Hooks can be easily extracted and reused:

If a hook is just a function… Can it be extracted from the component? YES! 🙆🏼

This way we’ve just extracted logic which uses internal state of the component, from the component itself.

The Effect Hook: useEffect

The Effect Hook lets you perform side effects in functional components.

If we wanted to show the counter value as the title of the page in the example above, we would have to do something like this without hooks:

The same example using hooks:

By using this Hook, you tell React that your component needs to do something after render, so React will call the effect after performing the DOM updates.

By default, React runs the effect after every render including the first one, but we can tell React to skip applying it if certain values haven’t changed between re-renders. To do so, we only have to pass an array as an optional second argument to useEffect:

In some cases we need our component to be subscribed to the changes in some external source. We normally do this by subscribing it to the source on componentDidMount and unsubscribing it on componentWillUnmount:

How can we do this with hooks?

The useEffect function can optionally return a function which is the cleanup mechanism for effects. React performs the cleanup when the component unmounts and before applying the next effects.

Pay attention to the second argument of the useEffect function in the example above. If we don’t define the second argument, the effect will be called after every re-render. To avoid so in the example, we passed an empty array as the second argument which will presumable not change over the re-renders so the effect will only be called after the first render and cleaned up when the component unmounts.

This way, we can think of useEffect Hook as componentDidMount, componentDidUpdate, and componentWillUnmount combined. Since we can use as many effects as we need, the code can be now arranged by concern instead of spread between the different lifecycle methods. I found this gif in twitter, which shows clearly the benefits of React Hooks, thanks to @prchdk:

Some rules with Hooks

Hooks are JavaScript functions, but you need to follow two rules when using them:

  • Only call hooks at the top level: Don’t call Hooks inside loops, conditions or nested functions.
  • Only call Hooks from React function components: Don’t call Hooks from regular Javascript functions except from your custom Hooks.

Custom Hooks

A custom Hook is a JavaScript function whose name starts with ”use” and that may call other Hooks. We define custom Hooks to extract and share logic between components.

In case you didn’t notice, we’ve already wrote one in the previous section “Hooks can be easily extracted and reused”:

Additional Hooks

There are much more Hooks that we don’t cover in this post:useContext, useReducer, useCallback, useRef, etc. Take a look to the Hooks API Reference doc to learn more about them.

What’s next

Since Hooks are completely opt-in and 100% backwards-compatible, we’re already trying them in some test projects. We’ll write about our thoughts soon so stay tuned!