visit
React Hooks opens a new world in the React ecosystem. However, it can be quite confusing for beginners working with React for the first time.
Therefore, in this tutorial, I’ll help you understand what React Hooks are, the various kinds of hooks available in React, and when and how to use them. Additionally, you will learn how to create custom hooks in React.
For example, just look at the code below and see how overwhelming it was to manage the state before the introduction of hooks. We had to manage states by binding them to the component using the JavaScript keyword.
So, finally, in the React v16.8 release, hooks were introduced to address these issues and provided a more elegant solution for functional components.
Fun Fact: Do you know you can create your own React hooks? Well, I also guide you through creating your custom hooks shortly.
For example, clicking on pause in a video player should pause the current playing video, or selecting a colour from the colour picker should select the current colour.
To do this, the components need to remember things here: the current state of the video and the current color value.
export default function ColorPicker() {
return (
<main>
<h1>React Color Picker</h1>
</main>
)
}
import { useState } from "react";
// const state = useState(); ❎ Cannot be use outside ColorPicker
export default function ColorPicker() {
const colorState = useState() //✅ Correct way of calling useState hook
console.log('Color State : ', colorState)
return (
<main>
<h1>React Color Picker</h1>
</main>
)
}
Color State : ▼ Array(2)
0 : undefined
1 : ƒ ()
import { useState } from "react";
export default function ColorPicker() {
// const colorState = useState()
const [color, setColor] = useState()
return (
<main>
<h1>React Color Picker</h1>
</main>
)
}
import { useState } from "react";
export default function ColorPicker() {
// const colorState = useState()
const [color, setColor] = useState('#8C00FF')
return (
<main>
<h1>React Color Picker</h1>
</main>
)
}
When the input value changes, the onColorChange() function is triggered. Inside this function, we call the setColor function and pass e.target.value as an argument to it.
import { useState } from "react";
export default function ColorPicker() {
const [color, setColor] = useState('#8C00FF')
function onColorChange(e){
setColor(e.target.value)
}
const divStyle = {
width : '200px',
height : '200px',
backgroundColor : color // color variable (#8c00ff)
}
return (
<main>
<h1>React Color Picker</h1>
<div style={divStyle}>
<h2>Color : {color}</h2>
</div>
<input type='color' value={color} onChange={onColorChange} />
</main>
)
}
useEffect Hook allows us to execute code after the component has been rendered. It is useful for tasks like data fetching in case you are not using any server data management library like React Query, etc. You can also use it for setting up subscriptions or handling side effects like using any external libraries, e.g., a video plugin.
useEffect(callbackFunction, dependencyArray);
Note: We can also neglect the dependency array, however it can cause major performance issues as the effect will run after every render of the component, including the initial one, without relying on any specific values.
import { useEffect } from "react";
export default function Todo() {
useEffect(() => {}, []);
//useEffect(callBackFunction, [dependenciesArray]);
return (
<div>
<h1>Todo App</h1>
</div>
)
}
import { useEffect } from "react";
export default function Todo() {
//callBackFunction to fetch todods
function fetchTodos(){
fetch('//jsonplaceholder.typicode.com/todos')
.then(response => response.json())
.then(json => console.log(json))
}
//useEffect(callBackFunction, [dependenciesArray]);
useEffect(fetchTodos, []);
return (
<div>
<h1>Todo App</h1>
</div>
)
}
.
import { useEffect } from "react";
export default function Todo() {
const [todos, setTodos] = useState([])
//callBackFunction to fetch todods
function fetchTodos(){
fetch('//jsonplaceholder.typicode.com/todos')
.then(response => response.json())
.then(json => setTodos(json))
}
//useEffect(callBackFunction, [dependenciesArray]);
useEffect(fetchTodos, []);
return (
<div>
<h1>Todo App</h1>
</div>
)
}
import { useEffect } from "react";
export default function Todo() {
const [todos, setTodos] = useState([])
//callBackFunction to fetch todods
function fetchTodos(){
fetch('//jsonplaceholder.typicode.com/todos')
.then(response => response.json())
.then(json => setTodos(json))
}
//useEffect(callBackFunction, [dependenciesArray]);
useEffect(fetchTodos, []);
return (
<div>
<h1>Todo App</h1>
{!todos.length && <h1>loading...</h1>}
<ul>
{todos?.map(({id, title}) => (
<li key={id}>{title}</li>
))}
</ul>
</div>
)
}
For example, suppose in a component of your app, there is a button that opens a modal, and also, there is a heavy calculation going on. When you open or close the modal, you may not want to run the heavy calculation.
const cachedValue = useMemo(calculateValue, dependencies)
const cachedFunction = useCallback(function, [dependencies])
Let’s look at the example below:
The useReducer hook is another great hook for managing states within React applications. However, unlike the useState hook, it is commonly used in components with numerous states across multiple event handlers.
The useReducer hook has four main components: the state, the reducer function, the action, and the dispatch function.
Let’s consider an example using a contact form that accepts a name, email address, and message from the users.
import { useReducer } from "react";
const ACTIONS = {
UPDATE_NAME: "updateName",
UPDATE_EMAIL: "updateEmail",
UPDATE_MESSAGE: "updateMessage",
};
const reducer = (state, action) => {
switch (action.type) {
case ACTIONS.UPDATE_NAME:
return { ...state, name: action.payload.value };
case ACTIONS.UPDATE_EMAIL:
return { ...state, email: action.payload.value };
case ACTIONS.UPDATE_MESSAGE:
return { ...state, message: action.payload.value };
default:
return state;
}
};
export default function App() {
const [state, dispatch] = useReducer(reducer, {
name: "",
email: "",
message: "",
});
const handleSubmit = (e) => {
e.preventDefault();
console.log({ state });
};
return (
<div>
<h2>Contact Us</h2>
<form onSubmit={handleSubmit}>
<label>Full Name</label>
<input
type='text'
value={state.name}
onChange={(e) => {
dispatch({
type: ACTIONS.UPDATE_NAME,
payload: {
value: e.target.value,
},
});
}}
/>
<label>Email Address</label>
<input
type='email'
value={state.email}
onChange={(e) => {
dispatch({
type: ACTIONS.UPDATE_EMAIL,
payload: {
value: e.target.value,
},
});
}}
/>
<label>Message</label>
<textarea
rows={6}
value={state.message}
onChange={(e) => {
dispatch({
type: ACTIONS.UPDATE_MESSAGE,
payload: {
value: e.target.value,
},
});
}}
/>
<button type='submit'>SEND</button>
</form>
</div>
);
}
const [state, dispatch] = useReducer(reducer, {
name: "",
email: "",
message: "",
});
When declaring the useReducer hook, it accepts two parameters – the reducer function and the state object.
const ACTIONS = {
UPDATE_NAME: "updateName",
UPDATE_EMAIL: "updateEmail",
UPDATE_MESSAGE: "updateMessage",
}
const reducer = (state, action) => {
switch (action.type) {
case ACTIONS.UPDATE_NAME:
return { ...state, name: action.payload.value };
case ACTIONS.UPDATE_EMAIL:
return { ...state, email: action.payload.value };
case ACTIONS.UPDATE_MESSAGE:
return { ...state, message: action.payload.value };
default:
return state;
}
};
<input type='text'
value={state.name}
onChange={(e) => {
dispatch({
type: ACTIONS.UPDATE_NAME,
payload: {
value: e.target.value,
},
});
}}
/>
For example, you might need a hook to determine if a user is online, to check if your app is running in the background, or for other specialized tasks.
One option is to write code in each of these components to monitor their visibility, but a more organized and efficient approach is to create a custom hook to handle this common task. In this way, we can abstract common functionalities, making our code cleaner and easier to manage, and that’s the main purpose of creating a custom hook.
Let’s understand the usePageVisibility hook :
import './App.css'
import { useState, useEffect } from "react";
// Custom hook to check if the tab is in the background
function usePageVisibility() {
const [isVisible, setIsVisible] = useState(true);
function handleVisibilityChange() {
setIsVisible(document.visibilityState === 'visible');
};
useEffect(() => {
document.addEventListener('visibilitychange', handleVisibilityChange);
return function(){
document.removeEventListener('visibilitychange', handleVisibilityChange);
};
}, []);
return isVisible;
}
export default function App() {
const isTabVisible = usePageVisibility();
return (
<div>
<h1>Check Page Visibility</h1>
<p>Is the tab visible? { isTabVisible ? 'Yes' : 'No' }</p>
</div>
);
}
It uses the useState hook to create a state variable isVisible, which represents whether the page is currently visible (it is true by default).
The handleVisibilityChange function is responsible for updating the isVisible state based on changes in the page’s visibility state (visible or hidden).
The useEffect hook is used to:
The hook returns the isVisible state, providing a boolean value to indicate whether the web page is currently visible to the user or not.
We are calling the usePageVisibility hook in the `App` and it is assigned to the isTabVisible, the value of isTabVisible will be either true or false. Based on this variable we are printing “Yes” or “ No” on the UI.
export default function App(){
// Call your hooks here
}
useEffect(() => {
// Missing dependencies can lead to bugs
}, []);
useEffect(() => {
// Causes an infinite render loop without dependency management
setState(state + 1);
}, []);
state.property = 'new value'; // Incorrect, don't modify state directly
const MyComponent = () => {
// Common logic duplicated in multiple components
useEffect(() => {
// Common logic
});
};
Here’s a quick recap of the key points we’ve covered in this post:
In this post, we explored two of the most commonly used hooks, useState and useEffect.
The useReducer hook is used for managing states within applications with complex state management and transitions.
You’ve also learned how to create custom hooks for specific tasks, such as checking if the web app tab is in the background.
Wander into the wonder; your next tech treasure awaits in my HackerNoon collection.
Also published .