A hook is just a special function in React that lets you "hook into" React features from inside a regular function component
In this post
- useState — remembering things
- useEffect — reacting to changes
- useContext — sharing data everywhere
- useRef — grabbing DOM elements
- useMemo & useCallback — staying fast
Hooks were introduced in React 16.8 and completely changed how we write components. Instead of complex class-based components, we now write simple functions and "hook into" React features. Let's go through the five you'll use every single day.
1. useState
- Lets your component remember a value — and update the UI when that value changes.
Think of it like a scoreboard. When a player scores, the number on the board updates automatically. useState is your scoreboard — it holds a value, and when it changes, React re-renders the component
// Import the useState Hook from React
import { useState } from 'react'
// Define a functional component named Counter
function Counter() {
// useState(0):
// - Creates a state variable called "count"
// - Creates a function called "setCount" to update "count"
// - Initializes count with the value 0
const [count, setCount] = useState(0)
// Return the JSX (UI) that will be rendered
return (
<div>
{/* Display the current value of count */}
<p>You clicked {count} times</p>
{/*
When the button is clicked:
1. Read the current value of count
2. Add 1 to it
3. Update the state using setCount()
4. React re-renders the component with the new value
*/}
<button onClick={() => setCount(count + 1)}>
{/* Text shown on the button */}
Click me
</button>
</div>
)
}
// Export the component so it can be imported into other files
export default Counter
Real-world uses
- Form inputs - Keep track of what the user is typing into an email or password field.
- Like button - Store the liked state and toggle it — exactly like a Twitter/X heart button.
Key rule: Never modify state directly (e.g. count = count + 1). Always use the setter function (setCount) so React knows to re-render
2. useEffect — reacting to changes
- Runs code after your component renders — perfect for fetching data, setting up subscriptions, or talking to the outside world.
Think of it like a notification trigger. When something happens (component loads, a value changes), a side effect kicks in — like fetching fresh data from an API.
// Import the useState and useEffect Hooks from React
import { useState, useEffect } from 'react'
// Define a functional component named WeatherApp
function WeatherApp() {
// Create a state variable named "weather"
// Initial value is null because no weather data has been loaded yet
const [weather, setWeather] = useState(null)
// useEffect runs after the component renders
useEffect(() => {
// Fetch weather data from the API
fetch('https://api.weather.com/today')
// Convert the response into JSON format
.then(res => res.json())
// Store the fetched data in the weather state
.then(data => setWeather(data))
}, []) // Empty dependency array [] means:
// Run this effect only once when the component first mounts
// If weather is still null, show a loading message
if (!weather) {
return <p>Loading...</p>
}
// Once weather data is available, display the temperature
return (
<p>It is {weather.temp}°C today</p>
)
}
// Export the component so it can be used in other files
export default WeatherApp
Avoid infinite loops: If you update state inside useEffect without a dependency array, it will keep running forever. Always add the [] or specific dependencies
3. useContext — sharing data everywhere
Lets any component access shared data — without passing props through every level of the tree
Think of it like a WiFi network. You set up a router (the context provider) once, and any device in range (any child component) can connect and get the internet — without running a cable from device to device
import { createContext, useContext, useState } from 'react'
// Create a Context object (shared data container)
const ThemeContext = createContext()
function App() {
// State to store the current theme
const [theme, setTheme] = useState('light')
return (
// Provide theme and setTheme to all child components
<ThemeContext.Provider value={{ theme, setTheme }}>
<Navbar />
<Page />
</ThemeContext.Provider>
)
}
function Navbar() {
// Consume (read) values from ThemeContext
const { theme, setTheme } = useContext(ThemeContext)
return (
// Toggle between 'light' and 'dark' themes
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle theme: {theme}
</button>
)
}
Real-world uses
- Dark mode - Share the current theme across every component without passing it as a prop everywhere.
- Logged-in user - Once the user logs in, share their name and profile across the entire app.
- useRef — grabbing DOM elements
- Gives you a direct reference to a DOM element — or stores a value that doesn't trigger a re-render when it changes.
Think of it like a sticky note on a physical object. You stick a label on an input box, and later you can point to it directly — without going through React's re-render cycle.
import { useRef } from 'react'
function SearchBar() {
// Create a ref object. Initially, current is null.
const inputRef = useRef(null)
function handleClick() {
// Access the actual <input> DOM element and place the cursor inside it
inputRef.current.focus()
}
return (
<div>
{/* Attach the ref to the input DOM element */}
<input ref={inputRef} placeholder="Search..." />
{/* When clicked, call handleClick to focus the input */}
<button onClick={handleClick}>Focus search</button>
</div>
)
}
Real-world uses
- Auto-focus - Focus a login input the moment a modal opens — better UX without re-rendering.
- Store a timer ID - Save setTimeout's return value so you can cancel it later — without triggering a re-render.
useState vs useRef: Use useState when changing the value should update the UI. Use useRef when you just need to remember a value or grab a DOM element — no UI update needed.
5. useMemo & useCallback — staying fast
- Cache expensive calculations or functions so React doesn't redo them on every render.
Think of it like mental notes. If someone asks you "what's 1247 × 38?" you calculate it once and remember the answer. If they ask again with the same numbers — you just give the answer from memory instead of recalculating
import { useState, useMemo } from 'react'
function ProductList({ products }) {
// State to store the user's search text
const [search, setSearch] = useState('')
// Memoize the filtered list and recalculate it
// only when products or search changes
const filtered = useMemo(() => {
return products.filter(p =>
p.name.toLowerCase().includes(search.toLowerCase())
)
}, [products, search])
return (
<div>
{/* Controlled input: value comes from state and updates state on typing */}
<input
value={search}
onChange={e => setSearch(e.target.value)}
/>
{/* Render only the filtered products */}
{filtered.map(p => (
<p key={p.id}>{p.name}</p>
))}
</div>
)
}
import { useCallback } from 'react'
function Parent() {
// Memoize the function and keep the same function reference
// between renders unless dependencies change
const handleClick = useCallback(() => {
console.log('button clicked!')
}, []) // [] = create the function once and reuse it on every render
// Pass the memoized function to the child component
return <ExpensiveButton onClick={handleClick} />
}
useMemo — filtered / sorted lists
Filtering 10,000 products every keypress is expensive. Cache the result until the data or search term changes.useCallback — stable event handlers
When passing a function to a child component wrapped in React.memo, keep the same reference to prevent unnecessary re-renders.
Don't over-use these. Wrapping everything in useMemo / useCallback adds its own cost. Only use them when you notice actual performance problems — measure first.
Quick reference summary
- useState - Store & update UI values
- useEffect - Side effects a fter render
- useContext - Share data app-wide
- useRef - Access DOM elements
- useMemo - Cache expensive values
- useCallback - Cache functions
Master these five and you'll be able to build almost any React app. The rest of the hooks exist for more specific or advanced scenarios — but these are the foundation. Happy coding! 🚀
For further actions, you may consider blocking this person and/or reporting abuse
