React Forum
Blog & Guide Responses
Feb 15, 2023 · 04:00 AM

Reusing logic with custom hooks - discussion (2023-02-15)

Started by Noah Vance
externalhookscustom hookscompositioncontractslanesevidenceintentdebuggability

The custom hooks article is one of those pieces that seems obvious until you see a codebase where every side effect moved into useWhatever() and nobody can answer where truth actually lives. I like hooks as a place to define a small vocabulary (lanes, intent, posture) as long as they don't become invisible controllers.

Read the article

When a hook returns logic, what do you prefer: data only, actions only, or a contract bundle (lane + data + actions + evidence)? Where do you draw the line between a custom hook and a route document/vault? Do you log hook decisions as contract lines, or do you insist that hooks produce UI evidence keys instead?

Comments (12)

Back to latest
Marisol Vega
Feb 18, 2023 · 06:06 AM

I like hooks that return a contract bundle, but only when the bundle is small and named.

If the hook returns 17 fields, it's not reuse—it's hidden coupling.

Dmitri Kline
Feb 21, 2023 · 04:04 AM

We stopped fighting about "is this hook too big" when we started logging hook decisions as a stable vocabulary. Not logs for data—logs for decisions:

txt
[tips] hook=useSearchContract lane=pending reason=query:commit qLen=5
[tips] hook=useSearchContract lane=ok reason=fetch:resolve resultCount=27

If the hook can't narrate its own transitions, it's usually doing orchestration that belongs at the route boundary instead.

Keira Santos
Mar 01, 2023 · 03:03 AM

Counterpoint: console logs are not a substitute for UI evidence.

I prefer hooks that return an evidence string (or write a stable data-*) so the user/support can see truth without devtools.

Noah Vance
Mar 05, 2023 · 05:05 AM

Agree. I like logs as dev-only, evidence as product.

The best hooks make the evidence inevitable (lane -> evidence line).

Grace McCoy
Mar 18, 2023 · 06:18 PM

Line between hook and vault for us: hooks can derive and adapt; vaults own identity and persistence.

If a hook starts "remembering" selection across navigation, it has become a store and should be treated like one.

Jasper Nguyen
Apr 06, 2023 · 06:06 AM

Long-form: reusable logic is only useful if it preserves *contract clarity*. I've seen teams extract hooks that hide important boundaries (commit vs draft, pending vs ok, identity changes). The UI becomes calmer in code, but the product becomes more confusing because behavior is no longer narratable.

A hook is good when it makes behavior more consistent across components while still letting you say: - what lane you're in, - why you're in that lane, - what identity boundary you're operating on.

If the hook erases those answers, it isn't reuse—it's amnesia.

Nina Kapoor
May 10, 2023 · 10:10 AM

We keep hooks boring by insisting on a strict output shape:

ts
type Contract<T> = { lane: 'idle' | 'pending' | 'ok' | 'error'; value?: T; evidence: string; act: { refresh(): void } };

Then the hook cannot "smuggle" in extra responsibilities without reviewers noticing.

Owen Price
Jun 01, 2023 · 06:06 AM

Counterpoint: sometimes lifting state to a parent is still the simplest reuse.

Hooks are great, but if the reuse is local and there's a clear parent, lifting can be clearer than introducing a hook surface.

Benji Rios
Jul 14, 2023 · 02:14 PM

Long-form counterpoint: hooks can become "soft singletons" if they read from module scope or cache across calls. If a hook has hidden global memory, you've created shared state without admitting it. That is a recipe for heisenbugs.

We required one invariant in code review: the hook must be referentially transparent given its inputs (or it must declare its external dependency as a contract and log it).

Lina Ortega
Aug 19, 2023 · 07:19 PM

Docs tie-in: the patterns + composition guidance pairs well with this approach of small, narratable contracts.

Related: Components and Composition and Patterns Library.

Theo Bennett
Oct 15, 2023 · 03:15 PM

We found hook reuse got better once we stopped trying to reuse UI state and started reusing *derived view models*.

View model hooks are less likely to become controllers, because they mostly compute and label.

Amina Farouk
Dec 20, 2023 · 06:18 PM

A good hook makes testing easier because it standardizes evidence.

If every feature uses the same lane/evidence vocabulary, tests can assert on behavior without learning each feature's private language.

Salma Qureshi
Dec 31, 2023 · 09:09 AM

If you're unsure whether to extract a hook, try writing a single [tips] line that would explain its behavior.

If you can't, you're probably extracting something that isn't a stable contract yet.