Help
Support Us

Hooks

The Hooks API is an alternative way to write components in Preact. Hooks allow you to compose state and side effects, reusing stateful logic much more easily than with class components.

If you've worked with class components in Preact for a while, you may be familiar with patterns like "render props" and "higher order components" that try to solve these challenges. These solutions have tended to make code harder to follow and more abstract. The hooks API makes it possible to neatly extract the logic for state and side effects, and also simplifies unit testing that logic independently from the components that rely on it.

Hooks can be used in any component, and avoid many pitfalls of the this keyword relied on by the class components API. Instead of accessing properties from the component instance, hooks rely on closures. This makes them value-bound and eliminates a number of stale data problems that can occur when dealing with asynchronous state updates.

There are two ways to import hooks: from preact/hooks or preact/compat.



Introduction

The easiest way to understand hooks is to compare them to equivalent class-based Components.

We'll use a simple counter component as our example, which renders a number and a button that increases it by one:

class Counter extends Component {
	state = {
		value: 0
	};

	increment = () => {
		this.setState(prev => ({ value: prev.value + 1 }));
	};

	render(props, state) {
		return (
			<div>
				<p>Counter: {state.value}</p>
				<button onClick={this.increment}>Increment</button>
			</div>
		);
	}
}
Run in REPL

Now, here's an equivalent function component built with hooks:

function Counter() {
	const [value, setValue] = useState(0);
	const increment = useCallback(() => {
		setValue(value + 1);
	}, [value]);

	return (
		<div>
			<p>Counter: {value}</p>
			<button onClick={increment}>Increment</button>
		</div>
	);
}
Run in REPL

At this point they seem pretty similar, however we can further simplify the hooks version.

Let's extract the counter logic into a custom hook, making it easily reusable across components:

function useCounter() {
	const [value, setValue] = useState(0);
	const increment = useCallback(() => {
		setValue(value + 1);
	}, [value]);
	return { value, increment };
}

// First counter
function CounterA() {
	const { value, increment } = useCounter();
	return (
		<div>
			<p>Counter A: {value}</p>
			<button onClick={increment}>Increment</button>
		</div>
	);
}

// Second counter which renders a different output.
function CounterB() {
	const { value, increment } = useCounter();
	return (
		<div>
			<h1>Counter B: {value}</h1>
			<p>I'm a nice counter</p>
			<button onClick={increment}>Increment</button>
		</div>
	);
}
Run in REPL

Note that both CounterA and CounterB are completely independent of each other. They both use the useCounter() custom hook, but each has its own instance of that hook's associated state.

Thinking this looks a little strange? You're not alone!

It took many of us a while to grow accustomed to this approach.

The dependency argument

Many hooks accept an argument that can be used to limit when a hook should be updated. Preact inspects each value in a dependency array and checks to see if it has changed since the last time a hook was called. When the dependency argument is not specified, the hook is always executed.

In our useCounter() implementation above, we passed an array of dependencies to useCallback():

function useCounter() {
	const [value, setValue] = useState(0);
	const increment = useCallback(() => {
		setValue(value + 1);
	}, [value]); // <-- the dependency array
	return { value, increment };
}

Passing value here causes useCallback to return a new function reference whenever value changes. This is necessary in order to avoid "stale closures", where the callback would always reference the first render's value variable from when it was created, causing increment to always set a value of 1.

This creates a new increment callback every time value changes. For performance reasons, it's often better to use a callback to update state values rather than retaining the current value using dependencies.

Stateful hooks

Here we'll see how we can introduce stateful logic into functional components.

Prior to the introduction of hooks, class components were required anywhere state was needed.

useState

This hook accepts an argument, this will be the initial state. When invoked this hook returns an array of two variables. The first being the current state and the second being the setter for our state.

Our setter behaves similar to the setter of our classic state. It accepts a value or a function with the currentState as argument.

When you call the setter and the state is different, it will trigger a rerender starting from the component where that useState has been used.

import { useState } from 'preact/hooks';

const Counter = () => {
	const [count, setCount] = useState(0);
	const increment = () => setCount(count + 1);
	// You can also pass a callback to the setter
	const decrement = () => setCount(currentCount => currentCount - 1);

	return (
		<div>
			<p>Count: {count}</p>
			<button onClick={increment}>Increment