React Tutorial 0/120 lessons ~6 min read Lesson 31

    Introduction to Hooks

    Introduction to React Hooks covers what Hooks are, why they replaced class-centric patterns, the use naming convention, built-in Hooks, custom Hooks, Rules of Hooks, and a practical mental model.

    Course progress0%
    Focus
    30 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    React Hooks are functions that let React function components use features such as state, effects, context, refs, and other React capabilities without writing class components.

    Hooks became a major part of modern React development because they allow component logic to be expressed directly inside function components while making reusable logic easier to organize.

    The most important idea to understand is:

    Hooks let function components use React features and share stateful logic in a structured way.

    Why Do We Need Hooks?

    Before Hooks, state and many other React features were primarily associated with class components.

    A class component could manage state like this:

    javascript
    class Counter extends React.Component {
    state = {
    count: 0
    };
    render() {
    return (
    <button onClick={() => this.setState({ count: this.state.count + 1 })}>
    {this.state.count}
    </button>
    );
    }
    }

    Modern React can express the same idea using a function component and useState:

    javascript
    import { useState } from "react";
    function Counter() {
    const [count, setCount] = useState(0);
    return (
    <button onClick={() => setCount(count + 1)}>
    {count}
    </button>
    );
    }

    The Hook-based version is generally easier to read and allows related logic to stay together.

    What Is a Hook?

    A Hook is a special React function whose name starts with use.

    Examples include:

    text
    useState
    useEffect
    useRef
    useMemo
    useCallback
    useReducer
    useContext

    You call Hooks from React function components or from custom Hooks.

    For example:

    javascript
    const [count, setCount] = useState(0);

    Here:

    text
    useState
    ↓
    React Hook
    0
    ↓
    Initial state
    count
    ↓
    Current state
    setCount
    ↓
    Function used to update state

    The `use` Naming Convention

    React Hooks use names beginning with:

    text
    use

    For example:

    javascript
    useState()
    useEffect()
    useRef()

    Custom Hooks should follow the same convention:

    javascript
    useOnlineStatus()
    useFetch()
    useLocalStorage()

    The use prefix is important because it communicates that a function follows Hook rules.

    Built-in Hooks

    React provides several built-in Hooks for different purposes.

    useState

    Used to add state to a function component.

    javascript
    import { useState } from "react";
    function Counter() {
    const [count, setCount] = useState(0);
    return (
    <button onClick={() => setCount(count + 1)}>
    Count: {count}
    </button>
    );
    }

    Use useState when a component needs to remember information that can change over time.

    useEffect

    Used to synchronize a component with an external system.

    javascript
    import { useEffect } from "react";
    function PageTitle() {
    useEffect(() => {
    document.title = "React Course";
    }, []);
    return <h1>React Course</h1>;
    }

    Effects are commonly used when React needs to interact with something outside the component's rendering process.

    Examples include:

    • browser APIs
    • subscriptions
    • timers
    • network-related synchronization
    • external systems

    useRef

    Used to keep a mutable value between renders without causing a re-render when that value changes.

    It is also commonly used to access a DOM element.

    javascript
    import { useRef } from "react";
    function SearchInput() {
    const inputRef = useRef(null);
    function focusInput() {
    inputRef.current.focus();
    }
    return (
    <>
    <input ref={inputRef} />
    <button onClick={focusInput}>
    Focus Input
    </button>
    </>
    );
    }

    useMemo

    Used to cache the result of a calculation between renders when appropriate.

    javascript
    import { useMemo } from "react";
    function ProductList({ products }) {
    const expensiveValue = useMemo(() => {
    return products.filter(product => product.active);
    }, [products]);
    return <div>{expensiveValue.length} active products</div>;
    }

    useMemo should be used when memoization provides a meaningful performance benefit. It should not be added automatically to every calculation.

    useCallback

    Used to cache a function definition between renders when appropriate.

    javascript
    import { useCallback } from "react";
    function Search({ onSearch }) {
    const handleSearch = useCallback(
    value => {
    onSearch(value);
    },
    [onSearch]
    );
    return <button onClick={() => handleSearch("React")}>
    Search
    </button>;
    }

    Like useMemo, useCallback is primarily a performance optimization and should be used when there is a specific reason for it.

    useReducer

    Used for managing state through a reducer function.

    javascript
    import { useReducer } from "react";
    function reducer(state, action) {
    switch (action.type) {
    case "increment":
    return { count: state.count + 1 };
    default:
    return state;
    }
    }
    function Counter() {
    const [state, dispatch] = useReducer(reducer, {
    count: 0
    });
    return (
    <button
    onClick={() => dispatch({ type: "increment" })}
    >
    Count: {state.count}
    </button>
    );
    }

    useReducer can be useful when state transitions are more complex than simple independent updates.

    Hooks and Function Components

    Hooks are designed to work with function components.

    A typical function component might look like:

    javascript
    function Profile() {
    return <h1>Profile</h1>;
    }

    With Hooks:

    javascript
    function Profile() {
    const [name, setName] = useState("");
    return (
    <input
    value={name}
    onChange={event => setName(event.target.value)}
    />
    );
    }

    The component remains a function, but it can now use React-managed state.

    Hooks Do Not Make Functions Stateful in the JavaScript Sense

    It is important to understand that Hooks do not turn a normal JavaScript function into a class-like object.

    React manages the Hook state and associates it with the component's rendering process.

    When you write:

    javascript
    const [count, setCount] = useState(0);

    React keeps track of that state across renders.

    Conceptually:

    text
    Initial render
    ↓
    useState(0)
    ↓
    count = 0
    User interaction
    ↓
    setCount(1)
    ↓
    React schedules an update
    ↓
    Component renders again
    ↓
    count = 1

    Rendering and Hooks

    A React component can render many times.

    For example:

    javascript
    function Counter() {
    const [count, setCount] = useState(0);
    console.log("Rendering");
    return (
    <button onClick={() => setCount(count + 1)}>
    {count}
    </button>
    );
    }

    Every state update can cause the component to render again.

    The Hook call:

    javascript
    useState(0)

    does not mean that 0 replaces the state on every render.

    The initial value is used when the state is initialized. React then preserves the current state across subsequent renders.

    Hooks and State Updates

    Consider:

    javascript
    const [count, setCount] = useState(0);

    When the user clicks:

    javascript
    setCount(count + 1);

    React schedules an update.

    The component renders again with the new state.

    text
    count = 0
    ↓
    setCount(1)
    ↓
    React schedules update
    ↓
    render
    ↓
    count = 1

    This is one of the most important mental models for understanding Hooks.

    Multiple Hooks in One Component

    A component can use multiple Hooks.

    javascript
    function UserProfile() {
    const [name, setName] = useState("");
    const [age, setAge] = useState(0);
    useEffect(() => {
    document.title = name || "Profile";
    }, [name]);
    return (
    <div>
    <input
    value={name}
    onChange={event => setName(event.target.value)}
    />
    <input
    type="number"
    value={age}
    onChange={event => setAge(Number(event.target.value))}
    />
    </div>
    );
    }

    Here the component uses:

    text
    useState
    ↓
    name
    useState
    ↓
    age
    useEffect
    ↓
    document title synchronization

    Each Hook has a different responsibility.

    Hooks and Component Logic

    Hooks allow related logic to stay close together.

    For example:

    javascript
    function Dashboard() {
    const [user, setUser] = useState(null);
    const [loading, setLoading] = useState(true);
    useEffect(() => {
    // Load user information
    }, []);
    return (
    <div>
    {loading
    ? "Loading..."
    : user?.name}
    </div>
    );
    }

    State and effect logic can be read directly alongside the UI that uses it.

    This is one of the major benefits of Hooks.

    Custom Hooks

    Hooks can also be composed into reusable custom Hooks.

    For example:

    javascript
    function useCounter(initialValue = 0) {
    const [count, setCount] = useState(initialValue);
    function increment() {
    setCount(value => value + 1);
    }
    function decrement() {
    setCount(value => value - 1);
    }
    return {
    count,
    increment,
    decrement
    };
    }

    Then:

    javascript
    function Counter() {
    const {
    count,
    increment,
    decrement
    } = useCounter(10);
    return (
    <div>
    <p>{count}</p>
    <button onClick={decrement}>
    -
    </button>
    <button onClick={increment}>
    +
    </button>
    </div>
    );
    }

    The custom Hook packages reusable stateful logic.

    Sharing Logic vs Sharing State

    This distinction is extremely important.

    Suppose two components use:

    javascript
    const [count, setCount] = useState(0);

    They do not automatically share the same count.

    Each component instance gets its own state.

    text
    Counter A
    ↓
    count = 5
    Counter B
    ↓
    count = 10

    A custom Hook shares logic, not automatically the same state instance.

    To share state between components, React provides other patterns such as:

    • lifting state up
    • Context
    • external state-management solutions

    Rules of Hooks

    Hooks have strict usage rules.

    The two fundamental rules are:

    Rule 1: Only Call Hooks at the Top Level

    Do not call Hooks inside:

    • loops
    • conditions
    • nested functions
    • event handlers

    Incorrect:

    javascript
    if (isLoggedIn) {
    const [user, setUser] = useState(null);
    }

    Correct:

    javascript
    const [user, setUser] = useState(null);
    if (!isLoggedIn) {
    return <Login />;
    }

    Rule 2: Only Call Hooks from React Functions

    Hooks should be called from:

    • React function components
    • custom Hooks

    For example:

    javascript
    function Profile() {
    const [name, setName] = useState("");
    // ...
    }

    A custom Hook:

    javascript
    function useUser() {
    const [user, setUser] = useState(null);
    // ...
    }

    Do not call a Hook from an ordinary utility function that is not a component or custom Hook.

    Why Hook Rules Exist

    React relies on the consistent ordering of Hook calls across renders.

    Consider:

    javascript
    function Component() {
    const [name, setName] = useState("");
    const [age, setAge] = useState(0);
    // ...
    }

    Conceptually:

    text
    Hook #1 → name
    Hook #2 → age

    If Hook calls could appear conditionally:

    javascript
    if (condition) {
    useState("");
    }
    useState(0);

    the order could change between renders.

    That would make it ambiguous which state belongs to which Hook call.

    Therefore, Hook calls must remain in a stable order.

    Hooks and Conditional Logic

    You can use conditions around the result of a Hook.

    Incorrect:

    javascript
    if (isAdmin) {
    const [name, setName] = useState("");
    }

    Correct:

    javascript
    const [name, setName] = useState("");
    if (!isAdmin) {
    return <AccessDenied />;
    }

    The Hook is called unconditionally, while the component's rendering logic can still be conditional.

    Hooks and Event Handlers

    Do not call Hooks inside event handlers.

    Incorrect:

    javascript
    function Button() {
    function handleClick() {
    const [count, setCount] = useState(0);
    }
    return (
    <button onClick={handleClick}>
    Click
    </button>
    );
    }

    Instead, call the Hook at the component level:

    javascript
    function Button() {
    const [count, setCount] = useState(0);
    function handleClick() {
    setCount(value => value + 1);
    }
    return (
    <button onClick={handleClick}>
    {count}
    </button>
    );
    }

    Hooks and JavaScript Functions

    A custom Hook is still a JavaScript function.

    For example:

    javascript
    function useCounter() {
    const [count, setCount] = useState(0);
    return {
    count,
    setCount
    };
    }

    The important distinction is that the function uses React Hooks, so it follows the Rules of Hooks.

    The use prefix makes its purpose clear.

    Hooks and Reusability

    Without custom Hooks, similar logic may be duplicated:

    javascript
    function ComponentA() {
    // online status logic
    }
    function ComponentB() {
    // same online status logic
    }

    With a custom Hook:

    javascript
    function useOnlineStatus() {
    // reusable logic
    }

    Components can use:

    javascript
    const isOnline = useOnlineStatus();

    This gives us:

    text
    Reusable behavior
    ↓
    Custom Hook
    ↓
    Multiple components

    Choosing the Right Hook

    A simple decision process can help.

    text
    Need component state?
    ↓
    useState
    text
    Need complex state transitions?
    ↓
    useReducer
    text
    Need to synchronize with an external system?
    ↓
    useEffect
    text
    Need a DOM reference or persistent mutable value?
    ↓
    useRef
    text
    Need to cache an expensive calculation?
    ↓
    useMemo
    text
    Need to cache a function reference?
    ↓
    useCallback
    text
    Need reusable stateful logic?
    ↓
    Custom Hook

    These are guidelines rather than rigid rules. The simplest solution should generally be preferred.

    Hooks Are Not a Replacement for Everything

    Not every piece of logic requires a Hook.

    For example, this is just normal JavaScript:

    javascript
    function formatPrice(price) {
    return `$${price.toFixed(2)}`;
    }

    There is no reason to create:

    javascript
    function useFormatPrice(price) {
    // unnecessary
    }

    Custom Hooks are useful when the logic actually involves React features or needs to coordinate with React's rendering lifecycle.

    Hooks and Derived Data

    Not every calculated value should become state.

    For example:

    javascript
    function Cart({ items }) {
    const total = items.reduce(
    (sum, item) => sum + item.price,
    0
    );
    return <p>Total: ${total}</p>;
    }

    There is no need for:

    javascript
    const [total, setTotal] = useState(0);

    when total can be calculated directly from existing data.

    A useful principle is:

    Don't store data in state when it can be derived reliably from existing props or state.

    Hooks and Side Effects

    A side effect is work that interacts with something outside React's rendering calculation.

    Examples include:

    text
    Browser APIs
    Timers
    Subscriptions
    External systems
    Network synchronization

    useEffect is designed for synchronizing with external systems.

    For example:

    javascript
    useEffect(() => {
    const connection = createConnection();
    connection.connect();
    return () => {
    connection.disconnect();
    };
    }, []);

    The cleanup function helps remove the external connection when the effect needs to be stopped or restarted.

    Hooks and Performance

    Some Hooks can help optimize performance:

    text
    useMemo
    useCallback

    But optimization should be based on an actual need.

    Don't automatically write:

    javascript
    const value = useMemo(() => 10 + 20, []);

    for a trivial calculation.

    Similarly, don't wrap every function in:

    javascript
    useCallback(...)

    without a reason.

    First write clear code.

    Then optimize where profiling or a known rendering pattern indicates a benefit.

    Hooks and Debugging

    When debugging Hooks, useful questions include:

    text
    Is the component rendering again?
    Is state updating?
    Are dependencies correct?
    Is an effect running when expected?
    Is a Hook being called conditionally?
    Is a custom Hook returning the expected value?

    React Developer Tools can also help inspect component state and understand component behavior.

    Common Mistakes

    1. Calling Hooks Conditionally

    Incorrect:

    javascript
    if (condition) {
    useEffect(() => {});
    }

    Keep the Hook call at the top level instead.

    2. Calling Hooks Inside Loops

    Incorrect:

    javascript
    for (const item of items) {
    useState(item);
    }

    Hooks should not be created dynamically in loops.

    3. Calling Hooks Inside Event Handlers

    Incorrect:

    javascript
    function handleClick() {
    useState(0);
    }

    Call the Hook in the component instead.

    4. Creating State for Derived Values

    Avoid unnecessary duplicated state.

    Instead of:

    javascript
    const [fullName, setFullName] = useState("");
    const fullName = firstName + " " + lastName;

    derive it directly:

    javascript
    const fullName = `${firstName} ${lastName}`;

    5. Using useEffect for Everything

    Not every calculation or state update belongs in an Effect.

    Use Effects when you need to synchronize with an external system.

    6. Overusing Memoization

    Don't use useMemo and useCallback simply because they exist.

    Use them when they solve a real performance or referential-identity problem.

    7. Confusing Custom Hooks with Shared State

    Calling the same custom Hook from two components generally creates separate Hook state for each component instance.

    Best Practices

    1. Follow the Rules of Hooks

    Keep Hook calls at the top level of React components and custom Hooks.

    2. Prefer Simple Hooks

    Use the simplest Hook that solves the problem.

    3. Keep Components Focused

    Don't create one enormous component containing unrelated state and effects.

    4. Extract Repeated Stateful Logic

    When the same React-specific logic appears in multiple components, consider creating a custom Hook.

    5. Avoid Unnecessary State

    Derive values from props and existing state whenever practical.

    6. Use Effects for External Synchronization

    Don't use useEffect merely because something happens after rendering.

    7. Optimize Intentionally

    Use useMemo and useCallback when there is a clear reason.

    8. Name Custom Hooks Clearly

    Prefer:

    text
    useOnlineStatus
    useWindowSize
    useLocalStorage
    useFetch

    over vague names.

    Real-World Example: Online Status

    A common reusable behavior is detecting whether the browser is online.

    A custom Hook could encapsulate that behavior:

    javascript
    import { useEffect, useState } from "react";
    function useOnlineStatus() {
    const [isOnline, setIsOnline] = useState(
    navigator.onLine
    );
    useEffect(() => {
    function handleOnline() {
    setIsOnline(true);
    }
    function handleOffline() {
    setIsOnline(false);
    }
    window.addEventListener(
    "online",
    handleOnline
    );
    window.addEventListener(
    "offline",
    handleOffline
    );
    return () => {
    window.removeEventListener(
    "online",
    handleOnline
    );
    window.removeEventListener(
    "offline",
    handleOffline
    );
    };
    }, []);
    return isOnline;
    }

    A component can now simply use:

    javascript
    function Status() {
    const isOnline = useOnlineStatus();
    return (
    <p>
    {isOnline
    ? "You are online"
    : "You are offline"}
    </p>
    );
    }

    The component doesn't need to know how the browser event listeners work.

    This is the power of custom Hooks:

    text
    Complex React logic
    ↓
    Custom Hook
    ↓
    Simple component API

    Hooks Mental Model

    Think of Hooks as a bridge between a function component and React's capabilities.

    text
    Function Component
    │
    ┌───────────┼───────────┐
    ▼ ▼ ▼
    useState useEffect useRef
    │ │ │
    ▼ ▼ ▼
    State Effects Refs
    │ │ │
    └───────────┼───────────┘
    ▼
    React

    And custom Hooks provide a way to package that logic:

    text
    React APIs
    ↓
    Custom Hook
    ↓
    Reusable logic
    ↓
    Multiple components

    Practice Exercises

    Exercise 1 — Counter with useState

    Create a counter with:

    • increment
    • decrement
    • reset

    Use useState.

    Exercise 2 — Document Title with useEffect

    Create a component that updates the browser document title whenever a counter changes.

    Exercise 3 — Focus Input with useRef

    Create an input and a button.

    Clicking the button should focus the input.

    Exercise 4 — Derived Data

    Create a shopping cart.

    Calculate the total from the cart items without storing the total separately in state.

    Exercise 5 — Custom Counter Hook

    Create:

    javascript
    useCounter()

    that returns:

    text
    count
    increment
    decrement
    reset

    Use it in two different components.

    Observe that each component has its own state.

    Exercise 6 — Online Status Hook

    Create:

    javascript
    useOnlineStatus()

    and display:

    text
    Online

    or:

    text
    Offline

    based on the browser's connection state.

    Interview questions & answers

    Interview preparation

    12 questions grouped by difficulty — expand any card for a model answer and follow-up probe.

    Beginner

    4
    1. 1What are React Hooks?
      Beginner

      Model answer

      Hooks are React functions that allow function components and custom Hooks to use React features such as state, effects, refs, and other capabilities.

    2. 2What is useEffect?
      Beginner

      Model answer

      useEffect is used to synchronize a component with an external system.
    3. 3Do custom Hooks share state?
      Beginner

      Model answer

      No.

      Calling the same custom Hook from different component instances normally gives each instance its own Hook state.

    4. 4Should every function use useCallback?
      Beginner

      Model answer

      No. useCallback should be used when caching a function reference has a meaningful purpose, such as working with memoized children or Hook dependencies.

    Intermediate

    4
    1. 5Why were Hooks introduced?
      Intermediate

      Model answer

      Hooks provide a way to use React features in function components and make reusable stateful logic easier to organize.

    2. 6What is useRef?
      Intermediate

      Model answer

      useRef provides a persistent mutable reference and is commonly used for accessing DOM elements without causing a re-render when the ref value changes.
    3. 7What are the Rules of Hooks?
      Intermediate

      Model answer

      Hooks should be called at the top level of React function components or custom Hooks, not inside conditions, loops, nested functions, or event handlers.

    4. 8Should derived values be stored in state?
      Intermediate

      Model answer

      Usually not.

      If a value can be calculated reliably from existing props or state, it can often be derived directly.

    Advanced

    4
    1. 9What is useState?
      Advanced

      Model answer

      useState is a Hook used to add state to a function component.
    2. 10What is a custom Hook?
      Advanced

      Model answer

      A custom Hook is a reusable JavaScript function whose name starts with use and that can use other Hooks to encapsulate reusable React-specific logic.
    3. 11Should every calculation use useMemo?
      Advanced

      Model answer

      No. useMemo is a performance optimization and should be used when memoization provides a meaningful benefit.
    4. 12Can Hooks be used in class components?
      Advanced

      Model answer

      The Hooks API is designed for function components and custom Hooks, not for class components.

    Quick Cheat Sheet

    HookPrimary Purpose
    useStateComponent state
    useEffectExternal synchronization
    useRefPersistent mutable reference / DOM reference
    useMemoCache a calculated value
    useCallbackCache a function reference
    useReducerManage complex state transitions
    Custom HookReuse React-specific logic

    Basic state

    javascript
    const [count, setCount] = useState(0);

    Effect

    javascript
    useEffect(() => {
    // synchronize with an external system
    }, []);

    Ref

    javascript
    const inputRef = useRef(null);

    Memoized calculation

    javascript
    const result = useMemo(
    () => calculate(value),
    [value]
    );

    Memoized callback

    javascript
    const handleClick = useCallback(
    () => doSomething(value),
    [value]
    );

    Reducer

    javascript
    const [state, dispatch] = useReducer(
    reducer,
    initialState
    );

    Custom Hook

    javascript
    function useSomething() {
    // Hook logic
    }

    Fundamental rule

    text
    Call Hooks
    ↓
    at the top level
    ↓
    of React components or custom Hooks

    Key Takeaways

    Ready to mark this lesson complete?Track your journey across the entire course.