惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
D
Docker
GbyAI
GbyAI
B
Blog RSS Feed
博客园 - 司徒正美
博客园 - Franky
美团技术团队
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
aimingoo的专栏
aimingoo的专栏
C
Check Point Blog
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
www.infosecurity-magazine.com
www.infosecurity-magazine.com
AI
AI
O
OpenAI News
Attack and Defense Labs
Attack and Defense Labs
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
S
Secure Thoughts
博客园 - 聂微东
L
LINUX DO - 最新话题
U
Unit 42
SecWiki News
SecWiki News
A
Arctic Wolf
Schneier on Security
Schneier on Security
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
V
Visual Studio Blog
量子位
The Cloudflare Blog
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
大猫的无限游戏
大猫的无限游戏
Google DeepMind News
Google DeepMind News
G
Google Developers Blog
T
Threat Research - Cisco Blogs
TaoSecurity Blog
TaoSecurity Blog
Recent Commits to openclaw:main
Recent Commits to openclaw:main
B
Blog
博客园 - 【当耐特】
C
CERT Recently Published Vulnerability Notes
Scott Helme
Scott Helme
Last Week in AI
Last Week in AI
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Microsoft Security Blog
Microsoft Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
A
About on SuperTechFans
博客园 - 三生石上(FineUI控件)
Latest news
Latest news

Echo JS

Desktop apps Introducing mermaid-lint: Stop Shipping Broken Diagrams How We Cut Slow Responses by 80% Migrating to Next.js App Router The quiet problem with unnecessary async - Matt Smith GitHub - ant-design/ant-design-cli: Ant Design on your command line. Query component knowledge, analyze project usage, and guide migrations — fully offline. Uncovering the Magic Behind Playwright React Performance Isn’t About useMemo — It’s About Render Boundaries T2 No Escape Hatches · Prickles GitHub - auto-agent-protocol/auto-agent-protocol: Automotive Agent Protocol GitHub - coactionjs/coaction: Zustand-style state management with built-in render tracking and cached computed state GitHub - williamtroup/Rattribute.js: ❓ A lightweight JavaScript library for automatically changing HTML element attributes based on responsive screen sizes. GitHub - williamtroup/Rink.js: 🔗 A JavaScript library for generating responsive HTML link targets. SVAR Kanban: Interactive Task Board Component for Svelte, React, and Vue Performance Cost of Popular 3rd Party Scripts date-light - Tiny date utilities for JavaScript GitHub - jskits/loggerjs: A faster, more powerful isomorphic logger Out Loud — Free AI Text to Speech Pocket DB — Embedded single-file NoSQL for Node.js billboard.js 4.0 release: Canvas rendering mode, 94.3% faster! GitHub - thegruber/linkpeek: Secure TypeScript link preview and URL metadata extractor for Open Graph, Twitter Cards, JSON-LD, Node/Bun/Deno/edge. GitHub - evoluteur/healing-frequencies: Play the healing frequencies of various sets of tuning forks: Solfeggio, Organs, Mineral nutrients, Ohm, Chakras, Cosmic octave, Otto, DNA nucleotides... or custom. Animated sine waves - 27 lines of pure JavaScript Framework | Neutralinojs GitHub - AllThingsSmitty/typescript-tips-everyone-should-know: ✅ A curated collection of practical TypeScript patterns that improve safety, readability, maintainability, and developer experience. 🧠 Heat.js : JavaScript Heat Map GitHub - iDev-Games/State-JS: State.js is a CSS‑reactive framework that makes UI state and updates flow through CSS instead of JavaScript logic. GitHub - yankouskia/gameplate: :video_game: Boilerplate for creating game with WebGL & Redux :game_die: GitHub - yankouskia/get-browser: 💻 Lightweight tool to identify the browser (mobile+desktop detection)📱 GitHub - yankouskia/is-incognito-mode: Identify whether browser is in incognito mode 👀
When React Hooks Stop Scaling: Moving Complex State to Zustand - Oren Farhi
Oren Farhi · 2026-07-09 · via Echo JS

In most of my React work, I reach for hooks first.

They’re usually the simplest way to keep state close to the UI and encapsulate behavior without introducing additional complexity.

Recently, I was building a speech recognition feature that streamed transcripts in real time, tracked whether the user was speaking, and exposed recognition errors to the UI.

The initial implementation lived inside a custom hook.

const {
  transcript,
  error,
  isUserSpeaking
} = useSpeechRecognition();

At first, that worked well.

Then requirements changed.

I needed transcript updates to be visible in multiple parts of the application. Some updates originated from browser speech recognition events, while others came from services outside the component tree.

That’s when things started getting messy.

The Bug That Made Me Reconsider

The first real warning sign wasn’t architectural.

It was a bug.

Users would occasionally see stale transcript data even though speech recognition events were still arriving.

At first I suspected a browser issue.

Then I suspected event ordering.

Then I spent several hours tracing state updates across effects, refs, callbacks, and component re-renders.

The actual problem was that multiple components were depending on the same state, but ownership of that state was still buried inside a hook that had originally been designed for a single UI flow.

Nothing was technically broken.

The design no longer matched the way the feature was being used.

The Hook Was Doing Too Much

I started with a custom hook because it felt like the simplest way to keep the speech recognition logic near the UI.

That worked until the same state needed to be read outside the original component tree and updated from code that didn’t belong to any one screen.

At that point, the hook wasn’t just reusable logic anymore.

It was acting like shared application state.

The hook eventually became responsible for:

  • Managing speech recognition lifecycle
  • Handling browser-specific events
  • Updating transcripts
  • Tracking speaking state
  • Reporting errors
  • Synchronizing data across multiple consumers

The public API still looked simple.

The implementation no longer was.

Why Context Wasn’t Enough

React Context was an option.

I considered it.

The problem wasn’t simply sharing state.

I also needed updates to originate outside React components.

Speech recognition events arrive asynchronously from browser APIs.

Some consumers only cared about transcript updates.

Others only cared about speaking status.

I wanted state updates to happen independently of React component ownership.

That pushed me toward a dedicated store.

Moving the State to Zustand

Instead of keeping everything inside a hook, I moved the state into Zustand.

const useRecognitionStore = create((set) => ({
  transcript: "",
  isUserSpeaking: false,

  setTranscript: (transcript) =>
    set({ transcript }),

  setSpeaking: (isUserSpeaking) =>
    set({ isUserSpeaking })
}));

The speech recognition service updates the store directly:

recognition.onresult = (event) => {
  useRecognitionStore
    .getState()
    .setTranscript(event.results[0][0].transcript);
};

Components subscribe only to the data they need:

const transcript = useRecognitionStore(
  state => state.transcript
);

That small change removed a surprising amount of complexity.

What Actually Improved

The biggest benefit wasn’t performance.

It was removing ambiguity around state ownership.

Before the migration, I regularly had to answer questions like:

  • Which component owns this state?
  • Why isn’t this update visible here?
  • Which effect updates this value?

After moving state into a store, those questions largely disappeared.

Speech recognition events update the store.

Components read from the store.

The flow became easier to follow because there was a single source of truth instead of state being hidden inside a hook implementation.

When I Still Prefer Hooks

I still use hooks extensively.

If state belongs to a component, useState is usually the right solution.

If I’m encapsulating reusable UI behavior, a custom hook is often exactly what I want.

But once state needs to be shared across unrelated parts of an application and updated from external systems, I start treating it as application state rather than component state.

That distinction has become a useful architectural guideline for me.

Final Thoughts

Nothing was wrong with the original hook.

It solved the problem it was originally designed for.

The problem changed.

The moment state became shared across multiple consumers and started receiving updates from outside React, the hook stopped being the right abstraction.

Moving that state into Zustand didn’t reduce complexity.

The complexity was already there.

It simply made the complexity visible and gave it a place to live.

Check out ReadM and try it out just for fun.