keepcalculator

06 Numbers and Powers

React Hooks: State, Effects & Custom Hooks Guide

Card No. 06·30389 · Updated · Standard formula, shown below

This guide walks you through using React Hooks to manage state, handle side effects, and create your own reusable custom hooks. You’ll learn the core hooks—useState, useEffect, and useContext—and how to combine them into custom hooks that keep your components clean and logic reusable. By the end, you’ll be able to refactor class components into functional ones and build your own hook library.

What You’ll Need

  • A React project set up (e.g., via Create React App, Vite, or Next.js) with React 16.8 or later.
  • Basic understanding of JavaScript (ES6+), especially functions, destructuring, and arrays.
  • Familiarity with React components (functional or class) is helpful but not required.

Step-by-Step

  1. Understand the Rules of Hooks

    Hooks must be called at the top level of your component, not inside loops, conditions, or nested functions. They must be called from React function components or custom hooks. This ensures that hooks are called in the same order every render, which React relies on to preserve state.

  2. Use useState for Local State

    Import useState from ‘react’. Call it with an initial value. It returns an array with the current state and a setter function. Example: const [count, setCount] = useState(0);. Update state by calling the setter with a new value or a function that takes the previous value: setCount(prev => prev + 1). This is the foundation for any dynamic UI.

  3. Add Side Effects with useEffect

    Import useEffect. It takes a function (the effect) and an optional dependency array. The effect runs after every render by default. With an empty array [], it runs only once after the initial render (like componentDidMount). With dependencies, it runs whenever those values change. Use it for data fetching, subscriptions, or manually changing the DOM. Always return a cleanup function if the effect creates a subscription or timer, to avoid memory leaks.

  4. Share Data with useContext

    To avoid prop drilling, create a context with createContext, wrap your component tree with a Provider, and consume it in any child with useContext. This is essential for global state like user authentication, theme, or language.

  5. Create a Custom Hook

    Custom hooks are JavaScript functions that start with ‘use’ and can call other hooks. They let you extract component logic into reusable functions. For example, a useFetch hook that handles loading, error, and data states. Write the hook as a separate file, import it where needed, and it will behave just like a built-in hook.

  6. Test and Refactor

    Once you have a working custom hook, test it in different components. Refactor existing class components by moving lifecycle logic into useEffect and state into useState. Ensure that the behavior remains identical.

Common Mistakes

  • Calling hooks conditionally – This breaks the rules and causes inconsistent state. Always call hooks at the top level.
  • Missing dependencies in useEffect – If you omit a dependency that the effect uses, you get stale closures. Include all values that the effect reads.
  • Infinite loops – Setting state inside an effect without specifying dependencies (or with a dependency that changes every render) can cause an infinite loop. Always check your dependencies.
  • Overusing useState for complex state – For related state, consider useReducer to manage transitions more predictably.
  • Forgetting cleanup – If you set up a timer or subscription, always clear it in the cleanup function to prevent memory leaks.

Tips

  • Use the eslint-plugin-react-hooks to automatically catch violations of the rules of hooks.
  • Keep custom hooks focused on a single piece of logic (e.g., useLocalStorage, useDebounce).
  • Name custom hooks starting with ‘use’ so React and other developers know it’s a hook.
  • When using useEffect for data fetching, consider a library like React Query for caching and revalidation, but you can also build a simple hook yourself.
  • Use the functional update form of setState when new state depends on previous state, especially in concurrent mode.

Troubleshooting

  • State not updating – Check if you are mutating state directly. Always use the setter function. If you need to update based on previous state, use the function form.
  • Effect runs too often or not enough – Review your dependency array. If you omit dependencies, the effect runs every render. If you include a value that changes every render (like an object literal), it will run every time. Use useMemo or useCallback to stabilize values.
  • Custom hook not working – Ensure the hook is called from a React component or another custom hook. Also, verify that the hook returns the expected values and that you are destructuring correctly.
  • “Too many re-rendersâ€� error – This usually happens when you call a setter during render. Move the state update into an event handler or an effect.
  • Stale closure in useEffect – If you see old values in your effect, add the missing dependencies or use a ref to hold the latest value.

Frequently Asked Questions

Can I use hooks in a class component?

No, hooks only work in function components or custom hooks. You can either convert your class to a function or use a wrapper component that uses hooks and passes props to the class.

What is the difference between useState and useReducer?

useState is simpler for independent state values. useReducer is better when you have complex state logic with multiple sub-values or when the next state depends on the previous one in a non-trivial way.

How do I fetch data with hooks?

Use useEffect with a fetch call inside, and manage loading/error/data with useState. Remember to abort the request in the cleanup function if the component unmounts.

Can I create a custom hook that uses other custom hooks?

Yes, custom hooks can call other hooks, including other custom hooks. This is a great way to compose logic.

How do I use useEffect to listen to window resize?

Add an event listener in the effect, update state with the new size, and remove the listener in the cleanup function. Use an empty dependency array to only set up once.

What is the purpose of the dependency array in useEffect?

It tells React when to re-run the effect. If the array is empty, the effect runs only once. If it contains values, the effect runs whenever any of those values change. Omitting it runs the effect after every render.

Related

Method: the standard formula for this calculation, rounded as each result states. See how the calculators work or report an error.

More cards in this drawer

The whole Numbers and Powers drawer →