in this article we are going to learn about custom hooks in react.
Custom hooks are basically functions, that allow users to define a hook for a specific purpose. Like a hook for fetching data, or a hook for checking whether a user has joined a chat room or not etc
Basically, you can extract and re use stateful logic from functional components with react custom hooks.
What are custom hooks: sharing logic between components
let us consider an application which relies heavily on the internet to function. In such an application it is important to notify the user if they are disconnected from the internet
We can define a component that tracks if the user is connected or not. For this we need
- A state variable that tracks if the user is online or not
- An effect that subscribes to the global
online
andoffline
events, and updates the state
You can turn on and off the network to see the component working.
Now, lets consider that we need to use this functionality in a different component.
Let's say we have a button that saves that app. We want this button to be disabled when the app is not connected to the internet.
Let us try to build this application
to start we can copy and paste the isOnline
state and the Effect into the SaveButton
If you are looking for a React Native Chat SDK to build in app chat messaging application, you can consider DeadSimpleChat React Native SDK
import { useState, useEffect } from 'react';
export default function SaveButton() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
function handleSaveClick() {
console.log('✅ App saved');
}
return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? 'Save progress' : 'You are Offline'}
</button>
);
}
These two components work fine but there is duplication of code. Even though both these components look different they have the same logic working behind the scenes
Extracting code to make a custom hook from a component
Imagine that there was a useOnlineStatus hook along with the useState and useEffect
Then we could easily remove duplication betweeen the components like so
function StatusBar() {
const isOnline = useOnlineStatus();
return <h1>{isOnline ? '✅ Online' : '❌ Offiline'}</h1>;
}
function SaveButton() {
const isOnline = useOnlineStatus();
function handleSaveClick() {
console.log('✅ App saved');
}
return (
<button disabled={!isOnline} onClick={handleSaveClick}>
{isOnline ? 'Save progress' : 'You are offline'}
</button>
);
}
There is no such built in hook but we can write the hook ourselves. Lets do it
- Declare a function and name is useOnlineStatus and copy all the duplicated code inside of it
- At the end of the function return
isOnline
. This lets your component read the values
function useOnlineStatus() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
function handleOnline() {
setIsOnline(true);
}
function handleOffline() {
setIsOnline(false);
}
window.addEventListener('online', handleOnline);
window.addEventListener('offline', handleOffline);
return () => {
window.removeEventListener('online', handleOnline);
window.removeEventListener('offline', handleOffline);
};
}, []);
return isOnline;
}
So, finally we have created the useOnlineStatus custom hook. Lets see how it works.
Here is the complete code:
useOnlineStatus:
App.js
We can verify the app is working by switching the network on and off
The components what they want to do and not how they want to do
when you extrac logic from the components you can hide the details on how you are access the external system like fetching the data from the internet using api or connecting to a database etc.
Example 2: useCounter
Let us consider a useCounter that implements a counter functionality.
Benefits of Custom Hooks
- Ability to re use code: With custom hooks you can easily re use code across multiple components. That reduces code duplication and improves maintainability
- Keeping the concerns seprated: Usually you need hooks to communicate with an external system. When using a hook inside a component the component does not need to know the details of how the custom hook is functioning and thus the concerns of the custom hook and the component remains seprated
- Improved Readibility: When the code for the hook is seprated from the component there is imporoved readability because there is less code overall ( As the code related to the custom hook has been already written in a separate file). This improve code readibility
- Easier to test: As the code for the custom hook can be tested seprately from the code of the component there is imporved readability
Why Hook names start with use
React components are build form hooks either custom hooks or built in hooks. You will often have to use hooks that are created by others or you might write one yourself
Here are the naming conventions that you must follow
- Component names must start with a capital letter: like so SaveButton or SaveStatus. React components must also return JSX that react and use to display on the screen
- Hook names must start with
use
and followed by a capital letter: like useEffect or useApiData etc. Hooks can return any value.
Following this convention you know where the components state, hook or other react features might hide
for examples: if you see a function called someColor
inside your component you can be sure that it can't contain react state because its name doesn't start with use . However a hook like useEffects might contain other hooks inside it
With custom hooks you can share stateful logic but not the state itself
In the top level component when you turned the network on and off the components are added together
however it is wrong to think that the single isOnline state variable is shared between them
let us look at the code
function StatusBar() {
const isOnline = useOnlineStatus();
// ...
}
function SaveButton() {
const isOnline = useOnlineStatus();
// ...
}
It works the same way as if you never extracted the code.
Now let us consider another hook:
function StatusBar() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// ...
}, []);
// ...
}
function SaveButton() {
const [isOnline, setIsOnline] = useState(true);
useEffect(() => {
// ...
}, []);
// ...
}
These are teo different state variables and Effects. They have the same value and the same time because they are synchronized with the same external resource
To explain this better let us consider a completely different examples
import { useState } from 'react';
export default function Form() {
const [firstName, setFirstName] = useState('Mary');
const [lastName, setLastName] = useState('Poppins');
function handleFirstNameChange(e) {
setFirstName(e.target.value);
}
function handleLastNameChange(e) {
setLastName(e.target.value);
}
return (
<>
<label>
Your First name:
<input value={firstName} onChange={handleFirstNameChange} />
</label>
<label>
Your Last name:
<input value={lastName} onChange={handleLastNameChange} />
</label>
<p><b>Hello, {firstName} {lastName}.</b></p>
</>
);
}
Here is some of the repetative logic from each form field
- the state variables firstName and lastName
- change handlers handleFirstNameChange and lastNameChange
- JSX specifies the value and onChange attribute for the input
We can extract the repitative logic into a useFormInput custom hook
App.js
useFormInput.js
import { useState } from "react";
export function useFormInput(initialValue) {
const [value, setValue] = useState(initialValue);
function handleChange(e) {
setValue(e.target.value);
}
const inputProps = {
value: value,
onChange: handleChange
};
return inputProps;
}
notice that we declare one state variable called as value
However, the Form component calls the useFormInput two times
function Form() {
const firstNameProps = useFormInput('Mary');
const lastNameProps = useFormInput('Poppins');
// ...
This is why it works like declaring two state variables.
Custom Hooks do not share the state itself but do share the stateful logic.
Each call to the hook is independent of all the other calls to the hook. If you need to share the state between two hooks then lift the state up and pass it down
Passing Reactive Values between hooks
Like components the custom hooks also need to be pure because the code inside of your custom hooks fire at every re render of the component
Pure meaning the out put of the hook should be the same for the same input values
Always think of the custom hooks code to be a part of the components body
Custom hooks recieve latest props and state because they re render with your component.
Chat room example
Lets re visit the chat room example to explore more in detail
chatroom.js
when the server url or the chat url changes the Effect reacts to the changes and re renders. You can tell by the console messages that the chat re connects every time the chat or the server url is different
Conclusion
In this article we learnt about the react custom Hooks. What are custom hooks and how you can make a custom hook of your own and how does the custom hooks works