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:
-
useState
accepts 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:
-
useEffect
is 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:
-
useContext
allows 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:
-
useReducer
is 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:
-
useRef
is 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:
-
useImperativeHandle
allows 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:
-
useLayoutEffect
runs 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:
-
useInsertionEffect
is 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:
-
useId
is 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:
-
useTransition
is 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:
-
useDeferredValue
defers 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:
-
useSyncExternalStore
ensures 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)