How to Reuse Stateful Logic Without Reusing State

Understand why custom Hooks share behavior, not values, and how each call to the same Hook keeps its own independent state.

6 min read

A custom Hook lets you reuse stateful logic across components, but it does not share the state itself. Each call to the same Hook creates its own independent values, which is exactly what makes the reuse safe. React re-runs the Hook body once per call, as if the logic were written inline in each component.

Show the logic you want to share

This component tracks a name field with its own state and a change handler. An email field would repeat the same pattern.

App.jsxApp.jsx
import { useState } from "react";
 
function Form() {
  const [name, setName] = useState("");
  function handleNameChange(event) {
    setName(event.target.value);
  }
  return (
    <label>
      Name <input value={name} onChange={handleNameChange} />
    </label>
  );
}

Every new field needs the same three pieces: a state variable, a handler, and a value binding. Only the label text and the state name differ.

Extract a Hook for one field

Move the state and the handler into a Hook that returns everything one input needs.

App.jsxApp.jsx
import { useState } from "react";
 
function useInput(initialValue) {
  const [value, setValue] = useState(initialValue);
  function handleChange(event) {
    setValue(event.target.value);
  }
  return [value, handleChange];
}

The useInput Hook contains the stateful part of the form. It returns the current value and the handler that updates it, so a component never touches useState directly for that field.

Two calls, two independent states

Call the Hook once per field. Each call gets its own state variable.

App.jsxApp.jsx
function Form() {
  const [name, handleNameChange] = useInput("");
  const [email, handleEmailChange] = useInput("");
  return (
    <div>
      <label>Name <input value={name} onChange={handleNameChange} /></label>
      <label>Email <input value={email} onChange={handleEmailChange} /></label>
    </div>
  );
}

Typing in the name field changes only the name state. The email field is separate. There is no shared variable hiding behind the two calls.

What is shared and what is not

  • Shared: the update logic, the return shape, and the behavior.
  • Not shared: the values themselves, including state and Effects.

React runs the Hook body once per call, so two calls behave like two independent copies of the same code. An Effect inside a custom Hook is duplicated the same way, so each call site gets its own subscription or timer.

Pass inputs to customize the logic

Arguments let each call configure the same behavior. useInput accepts an initial value, and a Hook can take any inputs, such as a fetch URL or a delay. The inputs also feed the Hook's dependency arrays, so a change re-runs the logic with fresh values.

When you really need shared state

If two components must reflect the same value, lift the state up to their common parent and pass it down as props. A custom Hook cannot turn two calls into one shared variable. Lifting state keeps one source of truth, so both components read and update the same value without drifting.

Use a Hook to share behavior, and use lifting state up to share values. See how to create a custom Hook for the extraction steps.

Rune AI

Rune AI

Key Insights

  • A custom Hook shares stateful logic, not state itself.
  • Each call to the same Hook creates independent state.
  • Pass arguments to configure behavior per call site.
  • Lift state up when components need the same value.
  • Use Hooks for behavior and props for shared values.
RunePowered by Rune AI

Frequently Asked Questions

Does a custom Hook share state between components?

No. Each call to a custom Hook creates its own independent state. Two components using the same Hook do not read from one shared value.

How do I share actual state between components?

Lift the state up to the nearest common parent and pass it down as props. A custom Hook shares logic, not values.

Can I pass arguments to a custom Hook?

Yes. Arguments let each call site configure the same logic, like a different initial value or delay.

Conclusion

Custom Hooks reuse stateful logic, not state itself. Every call is independent, which keeps the reuse safe. When two components need the same value, lift the state up and pass it down as props.