Understanding Hooks
By Syncfusion®, Inc. · Last updated
What hooks are, their benefits and rules, when to use them, the five groups of built-in hooks, a tour of useState, useEffect, useRef, useContext, useReducer, useMemo and useCallback, and how to choose the right one.
Introduction to Hooks
A hook is a special React function that gives a component an extra power, such as remembering a value or running code after the screen updates. Every hook's name starts with use.
You have already used one hook: useState. It gives a component the power to remember a value. React has more hooks, and each one gives a different power.
Real-World Example
A bookshop's book page must remember how many copies the user selected. A normal variable is reset every time the component runs again, so the number is lost.
The component calls the hook useState(1). React keeps the number between renders, and the page shows the new quantity each time the user clicks +.
A hook is a function whose name starts with use. You call it inside a component to give the component a new power.
Benefits of Hooks
Before hooks, many React powers were only available in class components, which were longer and harder to read. Hooks bring the same powers to normal function components.
- Shorter code — Function components with hooks need less code than classes.
- Easier to read — Code for one job sits together, not split across many places.
- Reusable logic — You can move hook code into your own hook and share it. You will learn this in the next lesson.
- Easier to test — Small functions are easier to check than big classes.
Rules of Hooks
Hooks must follow two rules. If you break them, React can mix up your hooks and show wrong data.
Rule 1: Call Hooks Only at the Top Level
Call hooks at the top of your component. Do not call them inside an if, a loop, or a nested function.
// Wrong: the hook is inside an if
if (isLoggedIn) {
const [name, setName] = useState("");
}
// Right: the hook is at the top. Use the if later.
const [name, setName] = useState("");
if (isLoggedIn) {
// use name here
}
React remembers hooks by their order: the first useState, the second useState, and so on. If a hook is inside an if, it may be skipped on some renders. Then the order changes, and React gives the wrong value to the wrong hook.
Rule 2: Call Hooks Only from React Functions
Call hooks only inside:
- a component (a function whose name starts with a capital letter), or
- a custom hook (a function whose name starts with
use).
Do not call hooks inside a normal JavaScript function.
Easy way to remember: hooks go at the top of a component or custom hook. Nowhere else.
Built-in Hook Categories
React's built-in hooks fall into five groups, based on what they do.
Five groups of hooks: State (useState, useReducer), Effect (useEffect), Reference (useRef), Context (useContext), Performance (useMemo, useCallback)
State useState useReducer
Effect useEffect
Reference useRef
Context useContext
Performance useMemo useCallback
The five groups of built-in hooks, and the common hooks in each group.
State Hooks
State hooks let a component remember data that changes the screen. They are useState and useReducer.
Effect Hooks
Effect hooks run code after React updates the screen, for work outside React. Examples: loading data, starting a timer, or changing the page title. The main one is useEffect.
Reference Hooks
Reference hooks remember a value without updating the screen when it changes. They can also hold a link to a real element on the page. The main one is useRef.
Context Hooks
Context hooks read data that is shared by many components, without passing props through every level. The main one is useContext.
Performance Hooks
Performance hooks help React skip work that is not needed, so the app stays fast. They are useMemo and useCallback.
Common Hooks Overview
This section shows each common hook in a short example.
useState
useState remembers a value. When you change it with the set function, React updates the screen. You learned it in the State lesson.
useState: count is 0, setCount(1) saves 1, React re-renders, the screen shows 1
count = 0 the value now
setCount(1) save a new value
Screen shows 1 React re-renders
useState keeps a value. Calling the set function saves a new value, and React shows it.
const [count, setCount] = useState(0);
useEffect
useEffect runs code after React shows the component on the screen. You give it a function, and a list of values to watch, called the dependency array.
useEffect runs after React renders and updates the screen
1. Render run the component
2. Update screen show the new TSX
3. Run effect your useEffect code
Effects run last: after React renders and updates the screen.
The console shows a message when the page first appears. Each click shows a new message, because count is in the list [count]. Lesson 13 explains effects in detail.
useRef
useRef gives you a box called a ref, with one property: current. You can put any value in ref.current. Changing it does not update the screen.
useRef: ref.current points to the real input element. Changing ref.current does not re-render
inputRef current: ●
points to
<input /> the real element on the page Changing ref.current does not update the screen.
A ref is a box with one property, current. Here it holds a link to the input element.
A common use is to hold a link to a real element, for example to move the cursor into an input box.
ref={inputRef} connects the ref to the input. After that, inputRef.current is the real input element. ?. means: call focus() only if the element is there.
useContext
useContext reads a value that a parent shares with all components below it. You do not need to pass the value as a prop through every level.
useContext: the Provider shares the theme. ThemedBadge reads it directly, Toolbar in the middle passes nothing
Provider value="dark" shares the theme
Toolbar passes nothing
ThemedBadge useContext → "dark"
The value goes straight from the Provider to the component that reads it. The middle component is skipped.
App shares theme with <ThemeContext.Provider value={theme}>. ThemedBadge reads it with useContext(ThemeContext). Toolbar, in the middle, does not pass any props.
useReducer
useReducer is like useState, but for state with many different changes. You write all the changes in one function, called a reducer. Then you call dispatch with an action that names the change.
useReducer: a button dispatches an action, the reducer works out the new count, the screen updates
dispatch { type: "add" }
cartReducer (count, action) → new count
count = 1 screen updates
Buttons only name the change. The reducer decides the new value.
Look at this line:
const [count, dispatch] = useReducer(cartReducer, 0);
cartReduceris the function that decides each change.0is the starting value.dispatch({ type: "add" })asks the reducer to do the "add" change.
All the cart rules live in one place: cartReducer.
useMemo
useMemo remembers the result of a calculation. It does the calculation again only when its inputs change. This saves time when the calculation is slow.
useMemo: if search changed, calculate again. If not, reuse the saved result
Render is search new?
yes
Filter the books again slow work, then save the result
no
Reuse the saved result no slow work
useMemo does the slow work only when its inputs change.
Type in the box. The console shows "Filtering..." each time. Now click Like the shop. The page updates, but the console shows nothing new. useMemo reused the old result, because search did not change.
useCallback
useCallback remembers a function, so the same function is used on every render. This matters when you pass the function to a child that only re-renders when its props change.
useCallback: render 1 and render 2 both use the same handleBuy function
Render 1
Render 2
handleBuy the same function
With useCallback, every render gets the same function, not a new one.
const handleBuy = useCallback((title: string) => {
console.log("Buying " + title);
}, []); // the same function every render
Without useCallback, a new handleBuy function is made on every render. You will use it in the performance lesson. For now, remember: useMemo remembers a value, useCallback remembers a function.
Choosing the Appropriate Hook
When you are not sure which hook to use, ask these questions in order:
- Does the screen need to change when this value changes? Use
useState. If there are many kinds of changes, useuseReducer. - Do you need to do something outside React, such as load data or start a timer? Use
useEffect. - Do you need to remember something without updating the screen, or reach a real element? Use
useRef. - Do many components at different levels need the same data? Use
useContext. - Is the app slow because of a big calculation or many re-renders? Then try
useMemooruseCallback.
Start with useState. Add the other hooks only when you have the problem they solve. Do not add useMemo and useCallback everywhere "just in case".
Common mistakes
| Mistake | What you see | Fix |
|---|---|---|
Calling a hook inside an if or loop | Error: Rendered more hooks than during the previous render | Call every hook at the top of the component |
| Calling a hook in a normal function | Error: Invalid hook call | Call hooks only in components or custom hooks |
Using useRef for data the screen shows | The screen does not update | Use useState for values the screen shows |
Forgetting the dependency array in useEffect | The effect runs after every render | Add [ ] with the values to watch |
Reading useContext without a Provider | You always get the default value | Wrap the components in <MyContext.Provider value={...}> |
Adding useMemo to every small value | Code is harder to read, and not faster | Use it only for slow calculations |
Check Yourself
Exercises
What to Remember
- Hooks are functions starting with
use. Call them only at the top of components or custom hooks. - State hooks remember data, effect hooks run code after rendering, refs remember without re-rendering, context shares data, and performance hooks skip work.
- Start with
useState. Reach for other hooks only when you have the problem they solve.
Next: Custom Hooks