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

    Custom Hooks

    Custom Hooks in React teach extracting reusable stateful logic, the use naming convention, sharing logic versus sharing state, and designing clear Hook APIs.

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

    Custom Hooks are one of the most powerful patterns for reusing stateful logic in React.

    React provides built-in Hooks such as:

    javascript
    useState
    useEffect
    useRef
    useMemo
    useCallback
    useReducer

    But real applications often contain logic that does not fit into a single built-in Hook.

    For example:

    • tracking online/offline status
    • fetching data
    • managing form fields
    • debouncing input
    • subscribing to browser events
    • synchronizing state with localStorage
    • detecting clicks outside an element
    • managing timers

    Instead of copying this logic between components, you can extract it into a Custom Hook.

    What Is a Custom Hook?

    A Custom Hook is a JavaScript function whose name starts with use and that can call other Hooks.

    For example:

    javascript
    function useOnlineStatus() {
    const [isOnline, setIsOnline] = useState(true);
    // Hook logic...
    return isOnline;
    }

    A component can use it like this:

    javascript
    function StatusIndicator() {
    const isOnline = useOnlineStatus();
    return <p>{isOnline ? "Online" : "Offline"}</p>;
    }

    The Custom Hook does not render UI.

    It encapsulates reusable stateful logic.

    Why Custom Hooks?

    Imagine two components need to track whether the browser is online.

    Without a Custom Hook, you might duplicate the same logic:

    javascript
    function Header() {
    // online status logic
    }
    function Footer() {
    // same online status logic
    }

    This creates several problems:

    • duplicated code
    • duplicated bug fixes
    • harder maintenance
    • inconsistent behavior
    • more difficult testing

    A Custom Hook extracts the logic:

    javascript
    function useOnlineStatus() {
    // shared logic
    }

    Now both components can use it:

    javascript
    function Header() {
    const isOnline = useOnlineStatus();
    // ...
    }
    javascript
    function Footer() {
    const isOnline = useOnlineStatus();
    // ...
    }

    The UI remains independent from the implementation details.

    Custom Hook Mental Model

    Think of a Custom Hook as a reusable logic layer.

    text
    Custom Hook
    ┌───────────────┐
    Component A → │ │
    │ Stateful Logic│
    Component B → │ │
    │ │
    Component C → │ │
    └───────────────┘

    More accurately, each component gets its own Hook state.

    text
    Component A
    ↓
    useOnlineStatus()
    ↓
    Hook state A
    Component B
    ↓
    useOnlineStatus()
    ↓
    Hook state B

    Calling the same Custom Hook from multiple components does not automatically create shared state.

    The logic is shared, but the state belongs to each component instance.

    Basic Custom Hook Syntax

    A Custom Hook is simply a function:

    javascript
    function useSomething() {
    // Hooks and logic
    }

    It usually:

    1. starts with use
    2. calls one or more Hooks
    3. performs reusable logic
    4. returns data, functions, or both

    Example:

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

    Usage:

    javascript
    function Counter() {
    const { count, increment } = useCounter();
    return (
    <button onClick={increment}>
    Count: {count}
    </button>
    );
    }

    Naming Convention

    Custom Hook names must begin with use.

    Good:

    javascript
    useCounter()
    useOnlineStatus()
    useForm()
    useFetch()
    useDebounce()
    useLocalStorage()

    Avoid naming a Hook like:

    javascript
    counter()
    onlineStatus()
    formManager()

    The use prefix communicates that the function follows the Rules of Hooks and may use React Hooks internally.

    A Custom Hook Can Use Other Hooks

    Custom Hooks can combine built-in Hooks.

    For example:

    javascript
    function useDocumentTitle(title) {
    useEffect(() => {
    document.title = title;
    }, [title]);
    }

    A component can now write:

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

    The component does not need to know how the document title is synchronized.

    Example: useOnlineStatus

    A browser exposes online/offline events.

    We can encapsulate them:

    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;
    }

    Now any component can use it:

    javascript
    function Header() {
    const isOnline = useOnlineStatus();
    return (
    <header>
    {isOnline ? "Online" : "Offline"}
    </header>
    );
    }

    Another component:

    javascript
    function SaveButton() {
    const isOnline = useOnlineStatus();
    return (
    <button disabled={!isOnline}>
    Save
    </button>
    );
    }

    The event subscription logic is implemented once.

    Custom Hooks Do Not Share State

    This is one of the most important concepts.

    Suppose:

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

    Two components use it:

    javascript
    function ComponentA() {
    const counter = useCounter();
    // ...
    }
    javascript
    function ComponentB() {
    const counter = useCounter();
    // ...
    }

    These are independent state instances.

    text
    Component A
    ↓
    useCounter()
    ↓
    count = 0
    Component B
    ↓
    useCounter()
    ↓
    count = 0

    If Component A changes its count, Component B's count does not automatically change.

    If you need shared state, consider:

    • lifting state up
    • Context
    • useReducer + Context
    • a dedicated state-management solution

    Custom Hook with Parameters

    Custom Hooks can accept arguments.

    Example:

    javascript
    function useDocumentTitle(title) {
    useEffect(() => {
    document.title = title;
    }, [title]);
    }

    Usage:

    javascript
    useDocumentTitle("Dashboard");

    Another component:

    javascript
    useDocumentTitle("Settings");

    Parameters allow one Hook to support different use cases.

    Custom Hook Returning Multiple Values

    A Hook can return an object:

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

    Usage:

    javascript
    const {
    count,
    increment,
    decrement,
    reset,
    } = useCounter(10);

    This is useful when the Hook exposes a small API.

    Returning Arrays vs Objects

    A Custom Hook can return either an array or an object.

    Array

    javascript
    return [count, increment];

    Usage:

    javascript
    const [count, increment] = useCounter();

    Arrays are convenient when the return values have a clear positional meaning.

    Object

    javascript
    return {
    count,
    increment,
    };

    Usage:

    javascript
    const { count, increment } = useCounter();

    Objects are often easier to read when a Hook returns several values.

    For example:

    javascript
    return {
    data,
    error,
    loading,
    refetch,
    };

    Example: useToggle

    A small Custom Hook can encapsulate Boolean state.

    javascript
    import { useCallback, useState } from "react";
    function useToggle(initialValue = false) {
    const [value, setValue] = useState(initialValue);
    const toggle = useCallback(() => {
    setValue((current) => !current);
    }, []);
    return {
    value,
    toggle,
    setValue,
    };
    }

    Usage:

    javascript
    function Menu() {
    const { value: isOpen, toggle } = useToggle();
    return (
    <div>
    <button onClick={toggle}>
    {isOpen ? "Close" : "Open"}
    </button>
    {isOpen && <div>Menu content</div>}
    </div>
    );
    }

    The component focuses on UI while the Hook manages the toggle behavior.

    Example: usePrevious

    A Custom Hook can encapsulate the common pattern of remembering the previous value.

    javascript
    import { useEffect, useRef } from "react";
    function usePrevious(value) {
    const ref = useRef();
    useEffect(() => {
    ref.current = value;
    }, [value]);
    return ref.current;
    }

    Usage:

    javascript
    function Price({ value }) {
    const previousValue = usePrevious(value);
    return (
    <div>
    <p>Current: {value}</p>
    <p>Previous: {previousValue}</p>
    </div>
    );
    }

    This combines:

    • useRef
    • useEffect
    • reusable logic

    into one abstraction.

    Example: useLocalStorage

    A Custom Hook can synchronize state with localStorage.

    javascript
    import { useEffect, useState } from "react";
    function useLocalStorage(key, initialValue) {
    const [value, setValue] = useState(() => {
    const storedValue = localStorage.getItem(key);
    return storedValue !== null
    ? JSON.parse(storedValue)
    : initialValue;
    });
    useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
    }, [key, value]);
    return [value, setValue];
    }

    Usage:

    javascript
    function Settings() {
    const [theme, setTheme] = useLocalStorage(
    "theme",
    "light"
    );
    return (
    <button
    onClick={() =>
    setTheme(theme === "light" ? "dark" : "light")
    }
    >
    Theme: {theme}
    </button>
    );
    }

    This turns localStorage synchronization into a reusable state abstraction.

    Example: useDebounce

    Debouncing is useful when an operation should wait until a value stops changing.

    For example:

    text
    User types:
    R
    Re
    Rea
    Reac
    React
    Instead of processing every change,
    wait until typing pauses.

    A simple Hook:

    javascript
    import { useEffect, useState } from "react";
    function useDebounce(value, delay = 500) {
    const [debouncedValue, setDebouncedValue] =
    useState(value);
    useEffect(() => {
    const timer = setTimeout(() => {
    setDebouncedValue(value);
    }, delay);
    return () => {
    clearTimeout(timer);
    };
    }, [value, delay]);
    return debouncedValue;
    }

    Usage:

    javascript
    function Search() {
    const [query, setQuery] = useState("");
    const debouncedQuery = useDebounce(query, 500);
    useEffect(() => {
    if (!debouncedQuery) {
    return;
    }
    // Perform search using debouncedQuery.
    }, [debouncedQuery]);
    return (
    <input
    value={query}
    onChange={(event) => setQuery(event.target.value)}
    />
    );
    }

    The debounce behavior is reusable across search fields, filters, and other inputs.

    Custom Hooks and Data Fetching

    A common real-world use case is encapsulating request state.

    A simplified Hook might expose:

    javascript
    const {
    data,
    error,
    loading,
    refetch,
    } = useFetch("/api/products");

    The Hook could internally manage:

    text
    request
    ↓
    loading
    ↓
    success / error

    For example:

    javascript
    function useFetch(url) {
    const [data, setData] = useState(null);
    const [error, setError] = useState(null);
    const [loading, setLoading] = useState(true);
    // Effect and request logic...
    return {
    data,
    error,
    loading,
    };
    }

    The component can focus on rendering:

    javascript
    function Products() {
    const {
    data,
    error,
    loading,
    } = useFetch("/api/products");
    if (loading) {
    return <p>Loading...</p>;
    }
    if (error) {
    return <p>Unable to load products.</p>;
    }
    return <ProductList products={data} />;
    }

    In production applications, data-fetching libraries may provide caching, retries, request deduplication, and other capabilities. A Custom Hook is still useful for feature-specific data logic.

    Custom Hooks and useReducer

    Custom Hooks can combine useReducer with other Hooks.

    For example, a request Hook could manage:

    javascript
    const initialState = {
    data: null,
    error: null,
    loading: false,
    };

    And actions:

    text
    requestStarted
    requestSucceeded
    requestFailed

    The Hook becomes a reusable feature-level state abstraction:

    text
    Component
    ↓
    useApi()
    ↓
    useReducer()
    ↓
    request state
    +
    useEffect()
    ↓
    API

    This is a powerful pattern for larger React applications.

    Custom Hooks and useCallback

    Sometimes a Custom Hook returns functions.

    For example:

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

    useCallback can be useful when the returned function needs stable identity, particularly when consumers pass it to memoized children or use it as a dependency.

    However, do not wrap every returned function in `useCallback automatically.

    Use it when referential stability provides a meaningful benefit.

    Custom Hooks and useMemo

    A Custom Hook can also use useMemo when it performs an expensive calculation.

    javascript
    function useFilteredProducts(products, query) {
    return useMemo(() => {
    return products.filter((product) =>
    product.name
    .toLowerCase()
    .includes(query.toLowerCase())
    );
    }, [products, query]);
    }

    Usage:

    javascript
    const filteredProducts =
    useFilteredProducts(products, query);

    The Hook provides a reusable abstraction while useMemo handles the performance optimization.

    Custom Hooks and Rules of Hooks

    Custom Hooks must follow the Rules of Hooks.

    Hooks should be called:

    • at the top level
    • consistently on every render
    • inside React function components or Custom Hooks

    Good:

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

    Avoid:

    javascript
    function useCounter(enabled) {
    if (enabled) {
    const [count, setCount] = useState(0);
    }
    // ...
    }

    Do not conditionally call Hooks.

    Instead:

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

    The Hook itself is still called consistently.

    Custom Hooks Should Represent a Meaningful Abstraction

    Not every piece of code needs a Custom Hook.

    This:

    javascript
    const [name, setName] = useState("");

    does not automatically need to become:

    javascript
    function useName() {
    // ...
    }

    A Custom Hook is valuable when it represents reusable logic.

    Good examples:

    javascript
    useOnlineStatus()
    useDebounce()
    useLocalStorage()
    usePrevious()
    useToggle()

    These names communicate a meaningful behavior.

    Custom Hook API Design

    A good Custom Hook should expose a small, understandable API.

    For example:

    javascript
    const {
    data,
    loading,
    error,
    refetch,
    } = useProducts();

    The component should not need to know:

    • which API client is used
    • how requests are cancelled
    • how loading is tracked
    • how errors are normalized
    • how caching works internally

    This is abstraction.

    The Hook exposes what the component needs, not every implementation detail.

    Keep Custom Hooks Focused

    Avoid creating a giant Hook:

    javascript
    useEverything()

    that manages:

    • authentication
    • products
    • notifications
    • forms
    • theme
    • analytics
    • WebSocket connections

    Instead, create focused Hooks:

    javascript
    useAuth()
    useProducts()
    useNotifications()
    useTheme()
    useWebSocket()

    Focused Hooks are easier to:

    • understand
    • test
    • reuse
    • maintain
    • debug

    Custom Hooks and Component Responsibilities

    A useful architecture is:

    text
    Component
    ↓
    Presentation + user interaction
    ↓
    Custom Hook
    ↓
    Stateful behavior
    ↓
    Browser / API / external system

    For example:

    javascript
    function ProductSearch() {
    const {
    query,
    setQuery,
    results,
    loading,
    } = useProductSearch();
    return (
    // UI
    );
    }

    The component handles presentation.

    The Hook handles search behavior.

    Common Mistakes

    1. Thinking Custom Hooks Share State

    They don't automatically share state.

    Each Hook invocation gets its own state.

    2. Calling Hooks Conditionally

    Avoid:

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

    Call the Hook unconditionally and place conditions inside the Hook logic when appropriate.

    3. Creating Hooks for Every Small Function

    A normal helper function is often enough if it does not use Hooks.

    For example:

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

    This should not be a Custom Hook.

    4. Putting UI Inside a Custom Hook

    Custom Hooks generally return data and behavior, not JSX.

    Prefer:

    javascript
    const { isOnline } = useOnlineStatus();

    rather than building an entire UI component inside the Hook.

    5. Returning Too Much

    Avoid exposing internal implementation details.

    Instead of:

    javascript
    return {
    state,
    setState,
    internalTimer,
    internalRef,
    internalHandler,
    // ...
    };

    return only what consumers need.

    6. Overusing useCallback and useMemo

    Custom Hooks do not automatically need performance optimizations.

    Use useCallback and useMemo when there is a clear reason.

    Testing Custom Hooks

    A Custom Hook is regular JavaScript logic built on React Hooks.

    Testing strategy depends on what the Hook does.

    For pure state transitions extracted into a reducer, test the reducer directly.

    For Hooks involving rendering, effects, subscriptions, or state updates, use an appropriate React testing environment and test the behavior exposed to consumers.

    For example, instead of testing internal implementation details, verify that:

    text
    Initial value
    ↓
    User interaction
    ↓
    Hook behavior
    ↓
    Expected returned value

    This keeps tests focused on observable behavior.

    Best Practices

    1. Start Names with use

    javascript
    useToggle()
    useDebounce()
    useAuth()

    2. Follow the Rules of Hooks

    Never call Hooks conditionally or inside loops.

    3. Keep Hooks Focused

    One Hook should generally represent one reusable concern.

    4. Return a Clear API

    Return only the values and functions consumers need.

    5. Hide Implementation Details

    Components should not need to understand the internal logic.

    6. Avoid Unnecessary Abstraction

    Create a Custom Hook when logic is reusable or complex enough to benefit from separation.

    7. Prefer Composition

    Custom Hooks can combine:

    javascript
    useState
    useEffect
    useRef
    useMemo
    useCallback
    useReducer

    to create higher-level behavior.

    8. Preserve Dependency Correctness

    If a Hook uses Effects, Memoization, or Callbacks, make sure dependency arrays accurately represent the values used by those functions.

    9. Keep Side Effects Predictable

    Subscriptions, timers, browser APIs, and external systems should be handled appropriately, usually through Effects or event handlers.

    Custom Hook Composition

    One Custom Hook can use another Custom Hook.

    For example:

    javascript
    function useUserPreferences() {
    const [theme, setTheme] =
    useLocalStorage("theme", "light");
    return {
    theme,
    setTheme,
    };
    }

    Now the application has multiple layers:

    text
    useUserPreferences()
    ↓
    useLocalStorage()
    ↓
    useState()
    useEffect()

    This is called Hook composition.

    Composition allows complex behavior to be assembled from smaller reusable pieces.

    Real-World Example: Authentication

    A large application might expose:

    javascript
    const {
    user,
    loading,
    login,
    logout,
    } = useAuth();

    Internally, the Hook might combine:

    text
    useState
    useEffect
    useCallback
    Context
    API calls
    local storage

    The component does not need to understand all of those implementation details.

    For example:

    javascript
    function Header() {
    const {
    user,
    logout,
    } = useAuth();
    return (
    <header>
    <span>Welcome, {user.name}</span>
    <button onClick={logout}>
    Logout
    </button>
    </header>
    );
    }

    This creates a clean separation between UI and authentication behavior.

    Real-World Example: Window Size

    A reusable Hook can track browser dimensions:

    javascript
    import { useEffect, useState } from "react";
    function getWindowSize() {
    return {
    width: window.innerWidth,
    height: window.innerHeight,
    };
    }
    function useWindowSize() {
    const [size, setSize] = useState(getWindowSize);
    useEffect(() => {
    function handleResize() {
    setSize(getWindowSize());
    }
    window.addEventListener("resize", handleResize);
    return () => {
    window.removeEventListener("resize", handleResize);
    };
    }, []);
    return size;
    }

    Usage:

    javascript
    function Dashboard() {
    const { width, height } = useWindowSize();
    return (
    <p>
    Window: {width} × {height}
    </p>
    );
    }

    The resize subscription can now be reused anywhere.

    Custom Hooks in Large Applications

    A feature-oriented structure can keep Hooks close to the feature that owns them:

    text
    src/
    └── features/
    ├── auth/
    │ ├── useAuth.js
    │ ├── authReducer.js
    │ └── Login.jsx
    │
    ├── products/
    │ ├── useProducts.js
    │ ├── ProductList.jsx
    │ └── ProductDetails.jsx
    │
    └── cart/
    ├── useCart.js
    ├── cartReducer.js
    └── Cart.jsx

    Generic Hooks can live in a shared location:

    text
    src/
    └── hooks/
    ├── useDebounce.js
    ├── usePrevious.js
    ├── useLocalStorage.js
    └── useWindowSize.js

    The exact folder structure is a project decision. The important principle is to make ownership and reuse clear.

    Practice Exercises

    Exercise 1: useToggle

    Create:

    javascript
    useToggle(initialValue)

    It should provide:

    • current Boolean value
    • toggle function
    • ability to set the value

    Exercise 2: useDocumentTitle

    Create:

    javascript
    useDocumentTitle(title)

    The browser tab title should update whenever title changes.

    Exercise 3: usePrevious

    Create:

    javascript
    usePrevious(value)

    Display both current and previous values.

    Exercise 4: useLocalStorage

    Create:

    javascript
    useLocalStorage(key, initialValue)

    It should:

    • initialize from localStorage
    • update React state
    • synchronize changes back to localStorage

    Exercise 5: useDebounce

    Create:

    javascript
    useDebounce(value, delay)

    Use it in a search component.

    Exercise 6: useWindowSize

    Create:

    javascript
    useWindowSize()

    Display:

    text
    Width: ...
    Height: ...

    Update the values when the browser is resized.

    Exercise 7: useForm

    Build:

    javascript
    useForm(initialValues)

    It should provide:

    • form values
    • field change handler
    • reset function
    • submit support

    Try to make it reusable across multiple forms.

    Mini Project: Reusable Search Hook

    Build a reusable:

    javascript
    useSearch()

    Hook.

    It should manage:

    text
    query
    results
    loading
    error

    Suggested API:

    javascript
    const {
    query,
    setQuery,
    results,
    loading,
    error,
    clear,
    } = useSearch();

    Requirements:

    • manage the search input
    • debounce the query
    • perform a search request
    • show loading state
    • handle errors
    • clear the search
    • avoid unnecessary requests
    • clean up asynchronous work appropriately

    Possible architecture:

    text
    Search Component
    ↓
    useSearch()
    ↓
    useDebounce()
    ↓
    useEffect()
    ↓
    API
    ↓
    Search Results

    This project demonstrates how Custom Hooks can compose multiple pieces of React functionality.

    Interview questions & answers

    Interview preparation

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

    Beginner

    3
    1. 1What is a Custom Hook?
      Beginner

      Model answer

      A Custom Hook is a reusable JavaScript function whose name starts with use and that can use other React Hooks to encapsulate reusable stateful logic.
    2. 2Why must Custom Hooks start with use?
      Beginner

      Model answer

      The naming convention allows React tooling and developers to recognize that the function may use Hooks and must follow the Rules of Hooks.

    3. 3Do Custom Hooks replace Context?
      Beginner

      Model answer

      No.

      They solve different problems.

      A Custom Hook extracts reusable logic.

      Context provides a way to pass data through a component tree without manually passing props at every level.

      They are often used together.

    Intermediate

    3
    1. 4Do Custom Hooks share state?
      Intermediate

      Model answer

      No. Each invocation has its own state. Custom Hooks share logic, not automatically the same state.
    2. 5Can a Custom Hook return JSX?
      Intermediate

      Model answer

      It technically can return values of many kinds, but a Custom Hook should generally expose data and behavior rather than render UI.

      Components should be responsible for rendering.

    3. 6Can Custom Hooks use useReducer?
      Intermediate

      Model answer

      Yes. A Custom Hook can combine useReducer with other Hooks to create reusable feature-level state management.

    Advanced

    2
    1. 7Can a Custom Hook use other Custom Hooks?
      Advanced

      Model answer

      Yes. function usePreferences() { → const theme = useLocalStorage("theme", "light"); → // ... → } This is Hook composition.
    2. 8When should you create a Custom Hook?
      Advanced

      Model answer

      Create one when you have reusable stateful logic or a meaningful behavior that should be shared between components.

    Custom Hooks Cheat Sheet

    Basic Hook

    javascript
    function useSomething() {
    // Hook logic
    return value;
    }

    With parameters

    javascript
    function useSomething(value) {
    // logic
    return result;
    }

    With useState

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

    With useEffect

    javascript
    function useDocumentTitle(title) {
    useEffect(() => {
    document.title = title;
    }, [title]);
    }

    With useReducer

    javascript
    function useFeature() {
    const [state, dispatch] = useReducer(
    reducer,
    initialState
    );
    return {
    state,
    dispatch,
    };
    }

    With another Custom Hook

    javascript
    function usePreferences() {
    const [theme, setTheme] =
    useLocalStorage("theme", "light");
    return {
    theme,
    setTheme,
    };
    }

    Main principle

    text
    Reusable stateful logic
    ↓
    Custom Hook
    ↓
    Multiple components

    Key Takeaways

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