React hooks revolutionized the way we build components by allowing us to use state and other features in functional components. In this tutorial, we’ll dive deep into all the current React hooks, walking you through practical examples. By the end of this article, you'll have a solid understanding of how each hook works and how you can use them to build better React applications.
If you want to see this guide in video form, check out this video:
Introduction
React hooks are functions that let you "hook into" React's state and lifecycle features from function components. Before hooks, React only allowed these features in class components. Since the introduction of hooks, you can now write fully functional components with powerful capabilities, leading to cleaner and more readable code.
In this article, we’ll cover the most important hooks in React, from the basic useState to the advanced useSyncExternalStore. Each hook will be explained with a hands-on tutorial.
Project Setup
Let’s begin by setting up a simple React project using Create React App.
-
First, install Create Your React App if you don't have it:
npx create-vite Install any dependencies you may need (if you are planning to use additional libraries).
Open the project in your code editor, and start editing
App.js. We will demonstrate each hook here with live examples.
useState Hook Tutorial
The useState hook allows you to add state to your functional components. Here's how it works:
import React, { useState } from 'react';
function Counter() {
const [count, setCount] = useState(0); // Declare state
return (
<div>
<h1>{count}</h1>
<button onClick={() => setCount(count + 1)}>Increase</button>
<button onClick={() => setCount(count - 1)}>Decrease</button>
</div>
);
}
export default Counter;
Key Points:
-
useStateaccepts the initial state as an argument and returns an array with the state variable and the function to update it. - The state persists across re-renders of the component.
useEffect Hook Tutorial
The useEffect hook allows you to perform side effects in your components, such as data fetching, subscriptions, or manually changing the DOM.
import React, { useState, useEffect } from 'react';
function FetchData() {
const [data, setData] = useState(null);
useEffect(() => {
fetch('https://api.example.com/data')
.then(response => response.json())
.then(data => setData(data));
}, []); // Empty dependency array to run only once
return (
<div>
<h1>Fetched Data</h1>
<pre>{JSON.stringify(data, null, 2)}</pre>
</div>
);
}
export default FetchData;
Key Points:
-
useEffectis invoked after every render unless you provide a dependency array (like[]), which makes it run only once when the component mounts. - This hook is ideal for operations that don't directly affect the UI, such as data fetching.
useContext Hook Tutorial
The useContext hook allows you to subscribe to React context and access its value in your components.
import React, { useState, useContext } from 'react';
// Create context
const ThemeContext = React.createContext('light');
function ThemedComponent() {
const theme = useContext(ThemeContext); // Access context value
return <div>The current theme is {theme}</div>;
}
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={theme}>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
<ThemedComponent />
</ThemeContext.Provider>
);
}
export default App;
Key Points:
-
useContextallows functional components to consume context directly without needing a consumer component. - It simplifies state sharing across components without prop drilling.
useReducer Hook Tutorial
The useReducer hook is an alternative to useState for managing more complex state logic, especially when the state depends on previous state.
import React, { useReducer } from 'react';
function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
default:
return state;
}
}
function Counter() {
const [state, dispatch] = useReducer(reducer, { count: 0 });
return (
<div>
<h1>{state.count}</h1>
<button onClick={() => dispatch({ type: 'increment' })}>Increase</button>
<button onClick={() => dispatch({ type: 'decrement' })}>Decrease</button>
</div>
);
}
export default Counter;
Key Points:
-
useReduceris often used when the state is an object or array and requires complex logic to update. - The reducer function is responsible for returning the new state based on the action.
useRef Hook Tutorial
The useRef hook provides a way to reference a DOM element or store a mutable value that doesn't cause re-rendering when updated.
import React, { useRef } from 'react';
function FocusInput() {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.focus();
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleFocus}>Focus Input</button>
</div>
);
}
export default FocusInput;
Key Points:
-
useRefis used to access the DOM directly or keep a mutable reference across renders. - It does not trigger a re-render when the value changes.
useImperativeHandle Hook Tutorial
useImperativeHandle is used with forwardRef to customize the instance value exposed to parent components when using ref.
import React, { useImperativeHandle, useRef, forwardRef } from 'react';
const FancyInput = forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
},
}));
return <input ref={inputRef} />;
});
function App() {
const inputRef = useRef();
return (
<div>
<FancyInput ref={inputRef} />
<button onClick={() => inputRef.current.focus()}>Focus Input</button>
</div>
);
}
export default App;
Key Points:
-
useImperativeHandleallows you to modify the instance value exposed to the parent throughref. - This is useful when you need to expose only specific methods or properties from a child component.
useLayoutEffect Hook Tutorial
useLayoutEffect is similar to useEffect, but it fires synchronously after all DOM mutations. It is useful for reading and modifying the DOM before the browser repaints.
import React, { useLayoutEffect, useState } from 'react';
function LayoutEffectDemo() {
const [width, setWidth] = useState(0);
useLayoutEffect(() => {
setWidth(window.innerWidth); // Measure DOM before render
}, []);
return <div>Window width: {width}</div>;
}
export default LayoutEffectDemo;
Key Points:
-
useLayoutEffectruns synchronously after all DOM mutations, making it ideal for cases where you need to measure or mutate the DOM before it is painted.
useInsertionEffect Hook Tutorial
useInsertionEffect runs before all DOM mutations, useful for injecting styles or performing DOM manipulations.
import React, { useInsertionEffect } from 'react';
function InsertionEffectDemo() {
useInsertionEffect(() => {
// Custom DOM manipulation or style injection
document.body.style.backgroundColor = 'lightblue';
}, []);
return <div>Check the body background color!</div>;
}
export default InsertionEffectDemo;
Key Points:
-
useInsertionEffectis for performing operations that need to happen before DOM mutations, like injecting styles.
useId Hook Tutorial
useId provides a unique identifier that is stable across server and client renders.
import React, { useId } from 'react';
function InputWithId() {
const id = useId();
return (
<div>
<label htmlFor={id}>Input Label</label>
<input id={id} />
</div>
);
}
export default InputWithId;
Key Points:
-
useIdis useful for generating unique IDs, ensuring consistency in both server-side rendering (SSR) and client-side rendering (CSR).
useTransition Hook Tutorial
The useTransition hook allows you to manage slow transitions in UI without blocking interaction.
import React, { useState, useTransition } from 'react';
function TransitionDemo() {
const [isPending, startTransition] = useTransition();
const [value, setValue] = useState("");
return (
<div>
<input
type="text"
value={value}
onChange={(e) => startTransition(() => setValue(e.target.value))}
/>
{isPending && <div>Loading...</div>}
</div>
);
}
export default TransitionDemo;
Key Points:
-
useTransitionis great for handling background tasks or slow updates without blocking the user interface.
useDeferredValue Hook Tutorial
The useDeferredValue hook allows you to delay re-rendering non-urgent updates.
import React, { useState, useDeferredValue } from 'react';
function DeferredValueDemo() {
const [value, setValue] = useState("");
const deferredValue = useDeferredValue(value);
return (
<div>
<input
type="text"
value={value}
onChange={(e) => setValue(e.target.value)}
/>
<div>{deferredValue}</div>
</div>
);
}
export default DeferredValueDemo;
Key Points:
-
useDeferredValuedefers rendering a non-urgent value to improve performance, allowing more critical updates to render first.
useSyncExternalStore Hook Tutorial
useSyncExternalStore is used to read data from an external store and subscribe to updates.
import React, { useSyncExternalStore } from 'react';
function useStore() {
// Simulate external store
const state = { count: 42 };
return state;
}
function StoreComponent() {
const store = useSyncExternalStore(useStore);
return <div>Store Count: {store.count}</div>;
}
export default StoreComponent;
Key Points:
-
useSyncExternalStoreensures that your component syncs with external stores in a way that guarantees stability across renders.
Outro
Congratulations! You’ve now learned about all the current React hooks and how to use them effectively. React hooks provide you with a powerful toolkit for building modern, efficient, and easy-to-maintain React applications. Keep experimenting with these hooks to improve your React skills and create amazing apps.
Happy coding!
Top comments (0)