React custom hooks is nothing more than a JavaScript function. It encapsulates components logic, can be imported, exported and reused all over the application. If you have a repetitive hooks logic in components, it might be a good idea to extract it into a separate function and reuse all over the application. Also, a custom hook function can call other React hooks if needed.
Take for example this very simple counter application with a useState
hook. On every button click we updating the counter by incrementing or decrementing. Let's refactor and move its logic to a custom hook.
// App.jsx
function App() {
const [counter, setCounter] = React.useState(0);
const increment = () => setCounter(counter + 1);
const decrement = () => setCounter(counter - 1);
return (
<>
<h1>{counter}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</>
);
}
To make a custom hook out of this component logic, we need to extract useState
and handlers into a separate function and move it in a separate file. A custom hook name should start with a prefix use
, that will indicate the usage of hooks inside it:
// useCounter.js
import React from 'react';
export function useCounter() {
const [counter, setCounter] = React.useState(0);
const increment = () => setCounter(counter + 1);
const decrement = () => setCounter(counter - 1);
return { counter, increment, decrement };
}
The state and logic of the counter are now encapsulated into the function. It should return the counter and handlers, responsible for state modifications. Also do not forget to export the function.
In a current example, the function returns an object with state values, but it is not restricted only to objects. As it is only a simple JavaScript function, we can return any data type. It could be aswell an array:
// ...
return [counter, increment, decrement];
//...
The useCounter
custom hook can now be imported anywhere in the application. Each instance of the function will hold its own state. That means even if you invoke useCounter
function in the same application, each instance of the counter will hold it's own state, and other instances will not be affected.
The final result will look like this:
import { useCounter } from './useCounter.js';
function App() {
const { counter, increment, decrement } = useCounter();
return (
<>
<h1>{counter}</h1>
<button onClick={increment}>Increment</button>
<button onClick={decrement}>Decrement</button>
</>
);
}
Custom hooks can consist not only state logic, but also be responsible for authentication, data fetching, DOM manipulations. Before creating your own hook, have a look at usehooks.com for already built recipes, maybe you'll what you been looking for.
Top comments (5)
Nice Article. Thanks.
Does the state inside the custom hook can lead to component re-rendering ?
If yes does it mean that when a state full custom hook is used inside a component, the state of the custom hook can be considered belonging to the component state ?
What if i wanna use more then once useCounter in same component? Nice article by the way.
You could give the counter objects a unique name.
Instead of destructuring:
Name the objects:
Then use them like this:
Hi ! Just passing by in this old article :)
Instead of naming the objects
You can do that :
It is the another way to achieve this.
Bye :)
Plain and simple, love it