A handy primer on React Hooks / React Hooks is what happens when you bring a cannon to a knife fight
A little over a year ago, React 16.8 shipped with an additional API that lets developers use state and other features in React without writing a class. Known as ‘Hooks’, this additional API has grown in popularity amongst developers and is now a common feature in everything from open-sourced applications to enterprise apps.
Crucially though, React hooks are completely opt-in, which means there is no need to rewrite existing code. Hooks are also 100% backward compatible and don’t contain any breaking changes.
Why Use React Hooks?
Hooks were developed with the intention of solving a range of seemingly unconnected problems, which were hampering the evolution of React—a language that is not yet a decade old.
Hooks make it possible to:
- Reuse stateful logic between components
Hooks allow you to reuse logic between components without changing their architecture or structure. - Understand components easily
When components become larger and carry out many operations, they become increasingly difficult to understand. Hooks solve this problem by allowing developers to separate a given component into various smaller functions which are related to each other. - Navigate classes (without tearing your hair out)
To the novice, Classes in React can become quite confusing. To complicate matters further, computers also tend to get confused by Functions and Classes. For instance, minifies/uglifies don’t play well with Classes and can cause problems. With Hooks, developers can use more of React’s features without opting for Classes. This makes sense because when you really think about it, React components have always been conceptually similar to functions. In essence, Hooks embrace functions without discarding everything that is great about React.
Before going any further, there are two main ‘rules’ that need to be kept in mind.
- Make sure to not use Hooks inside loops, conditions, or nested functions;
- Only use Hooks from inside React Functions.
The Different Types of Hooks
React 16.8 ships with 10 in-built hooks, but the most basic and commonly used ones are:
useState()
The useState() hook allows developers to update, handle and manipulate state inside functional components without needing to convert it to a class component.
useEffect()
The useEffect() hook accepts a function that would contain effectual code. In functional components, effects like mutations, subscriptions, timers, logging, and other effects are not allowed to be placed inside a functional component. Doing so would lead to a lot of bugs and inconsistencies when the UI is rendered.
When useEffect() hook is deployed, the effectual function passed into it will execute right after the render has been displayed on the screen. By default, effects are executed after the render has been completed, but you can also execute them when certain values change.
useContext()
The useContext() hook accepts a context object, i.e. a value that is returned from React.createContext, and then returns the current context value as appropriate.
Prior to the introduction of the useContext hook, developers would need to set up a contextType or a to access the global state passed down from a provider in a class component.
useRef()
The useRef hook is a function that returns a mutable ref object whose .current property is initialized with the passed argument (initialValue). The returned object will persist for the full lifetime of the component.
Experienced developers will recognise the useRef hook as something that is used to access DOM nodes or React elements. However, it can also be used to keep any mutable value around similar to how you would use instance fields in classes.
Before vs. After: A Code Example
In order to demonstrate how effective Hooks can be, let’s try to build a simple counter. If we were to use Classes, this is how the code would look:
class Example extends React.Component { constructor(props) { super(props); this.state = { count: 0 }; } render() { return ( <div> <p>You clicked {this.state.count} times</p> <button onClick={() => this.setState({ count: this.state.count + 1 })}> Click me </button> </div> ); } }
But, if we were to rewrite this using Hooks, this is how it would look:
import React, { useState } from 'react'; function Example() { const [count, setCount] = useState(0); return ( <div> <p>You clicked {count} times</p> <button onClick={() => setCount(count + 1)}> Click me </button> </div> ); }
Note the following:
- Line 1: The useState Hook from React is imported. It lets us keep the local state in a function component.
- Line 4: Inside the Example component, a new state variable is declared by calling the useState Hook. It returns a pair of values, to which names can be given. We’ve called our variable count because it holds the number of button clicks. We initialize it to zero by passing 0 as the only useState argument. The second returned item is itself a function. It lets us update the count so we’ve named it setCount.
- Line 9: When the user clicks, we call setCount with a new value. React will then render the Example component again, passing the new count value to it.
Bringing It All Together
Here is a little tutorial on how to implement a few basic hooks. In order to illustrate how each hook can be used, we will be attempting to build a small app within React.
import React, { useState, useRef, useEffect } from "react"; export default function Alarm(props) { let snooze = 600; const intervalRef = useRef(); const [alarm, setAlarm] = useState(props.alarmInSec); useEffect(() => { const id = setInterval(function () { setAlarm(alarm => alarm - 1); }, 1000); intervalRef.current = id; return () => { clearInterval(intervalRef.current); } }, []); return ( <div> <p>{new Date(alarm * 1000).toISOString().substr(11, 8)}</p> <button onClick={() => setAlarm(alarm + snooze)}> SNOOZE ALARM </button> </div> ); }
Done! You, young padawan, are now on your way to becoming a great React ninja. Congratulations!