写完 8 篇状态管理文章,我对前端开发的 5 个新认知

22 阅读13分钟

我开始写这个系列时,以为"状态管理"就是选个库(Redux 还是 Zustand)的问题。写完 9 篇,我才意识到:状态管理是一个思维方式,而非技术选型。

这篇文章不是技术总结,而是我的认知转变记录

系列的初衷

最开始,我只是想整理一些不太熟悉的知识点:

  • encodeURIComponent 到底什么时候用?
  • 登录后怎么跳转回原页面?
  • 多标签页的登录状态如何同步?

我以为这些都是"小问题",查查文档就能解决。但真正梳理下来,发现背后涉及的思考远比想象的深。

AI 在这个过程中帮了很多——快速生成代码、解释概念、提供多种方案。但同时,AI 也暴露了我的很多盲区:

  • 我对很多"常识"的理解其实很浅
  • 我习惯性地追求"标准答案",但技术选型往往没有标准答案
  • 我缺少系统的思维框架

最大的收获不是"学会了什么工具",而是"学会了如何思考"。

接下来,我想分享写完这个系列后的 5 个核心认知转变。


认知 1:服务端数据不是"状态",是"远程缓存"

写文章前的认知

我以为服务端数据也是"状态",用 useState 管理就够了:

// 环境:React 18+
// 场景:获取用户信息

function UserProfile() {
  const [user, setUser] = useState(null);
  
  useEffect(() => {
    fetch('/api/user')
      .then(res => res.json())
      .then(setUser);
  }, []);
  
  return <div>{user?.name}</div>;
}

当时觉得:这不就是"状态管理"吗?有什么问题?

写第 5 篇时的顿悟

在写《用 useState 管理服务端数据?不如试试 React Query 来“避坑”》时,我突然意识到:服务端数据有特殊性。

客户端的 user 变量,只是服务器数据的缓存副本。真正的"真相源"(source of truth)在服务器。

这意味着:

  • 过期问题:数据可能已经在服务器更新了,但客户端还是旧的
  • 同步问题:多个组件都需要同一份数据,怎么保证一致?
  • 失效问题:用户在其他标签页修改了数据,当前页面怎么知道?
  • 重试问题:网络请求失败了,要不要自动重试?

这些问题,useState 都解决不了。

对比表格

维度客户端状态服务端数据
真相源客户端服务器
生命周期组件控制需要同步策略
失效判断不需要需要(staleTime)
适用工具useState/ZustandReact Query/SWR
缓存策略不需要需要(cacheTime)

这个认知的影响

现在我会这样写:

// 环境:React 18+ with @tanstack/react-query
// 场景:获取用户信息并处理缓存

import { useQuery } from '@tanstack/react-query';

function UserProfile() {
  const { data: user, isLoading, error } = useQuery({
    queryKey: ['user'],
    queryFn: () => fetch('/api/user').then(res => res.json()),
    staleTime: 5 * 60 * 1000// 5分钟内认为数据新鲜
    cacheTime: 10 * 60 * 1000// 10分钟后清除缓存
  });
  
  if (isLoading) return <div>Loading...</div>;
  if (error) return <div>Error: {error.message}</div>;
  
  return <div>{user?.name}</div>;
}

代码变多了,但解决的问题也多了:

  • ✅ 自动缓存,避免重复请求
  • ✅ 失效重新获取,保证数据新鲜
  • ✅ 全局共享,多个组件访问同一份数据
  • ✅ 自动重试,网络异常时优雅降级

延伸思考

这让我开始思考:为什么很多教程没有强调这个区别?

可能是因为大家习惯了"状态"这个词,忽略了本质。但在实际开发中,混淆"客户端状态"和"服务端数据"会导致很多问题:

  • 用户看到的数据不是最新的
  • 页面切换时反复请求同一个 API
  • 多个组件维护重复的数据副本

对读者的建议:

  • 重新审视你项目中的 useState
  • 问自己:这个数据的"真相源"在哪里?
  • 如果在服务器,考虑用 React Query 或 SWR

认知 2:受控组件不总是最好的

被打破的"常识"

我一直以为:

React 推荐受控组件,所以应该总是用受控组件

React 官方文档也确实强调了受控组件的好处:可以实时验证、可以控制输入等等。

在写第 8 篇时发现的问题

当我写《表单写到想摔键盘?聊聊前端常见的复杂状态场景》时,测试了一个 100 个输入框的表单:

// 环境:React 18+
// 场景:大型表单的受控组件实现

function LargeForm() {
  const [values, setValues] = useState({});
  
  // 问题:每次输入都会触发整个组件重渲染
  const handleChange = (index, value) => {
    setValues(prev => ({ ...prev, [index]: value }));
  };
  
  return (
    <form>
      {Array.from({ length: 100 }).map((_, i) => (
        <input
          key={i}
          value={values[i] || ''}
          onChange={(e) => handleChange(i, e.target.value)}
        />
      ))}
    </form>
  );
}

// 性能:用户输入时,100个输入框都重渲染
// 输入10个字符/秒 × 100个框 = 1000次渲染/秒

用 React DevTools Profiler 测试后,发现输入时明显卡顿。

React Hook Form 的选择

// 环境:React 18+ with react-hook-form
// 场景:大型表单的非受控实现

import { useForm } from 'react-hook-form';

function LargeForm() {
  const { register, handleSubmit } = useForm();
  
  const onSubmit = (data) => {
    console.log(data);
  };
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      {Array.from({ length: 100 }).map((_, i) => (
        <input key={i} {...register(`field${i}`)} />
      ))}
    </form>
  );
}

// 性能:输入时只有当前输入框重渲染
// React Hook Form 使用非受控组件 + 按需订阅

性能提升非常明显,输入时完全流畅。

新认知

技术方案没有"应该",只有"权衡"

对比场景:

场景推荐方案理由
小表单(<10 字段)受控组件简单直观,易于理解
大表单(>50 字段)非受控组件性能好,避免不必要的渲染
需要实时验证受控组件可以在输入时立即反馈
只在提交时验证非受控组件性能最优
需要动态控制输入受控组件可以程序化控制值

这教会我

  • 不要盲目跟随"最佳实践"
  • 理解每种方案背后的权衡
  • 根据具体场景选择合适的方案

对读者的建议:

  • 如果你的表单很大,试试 React Hook Form
  • 如果需要实时验证,受控组件仍然是好选择
  • 性能问题出现时,用 Profiler 实际测试,而非凭感觉

认知 3:Context 的性能陷阱被严重低估了

我之前的误解

看到很多文章说:

Context 可以避免 Props Drilling(属性透传)

我就觉得:遇到跨层级传递数据,就用 Context。

写第 3 篇和第 8 篇时发现的问题

// 环境:React 18+
// 场景:多个状态通过 Context 共享

import { createContext, useState } from 'react';

const AppContext = createContext();

function App() {
  const [user, setUser] = useState({ name: 'Alice' });
  const [theme, setTheme] = useState('light');
  
  // 问题:value 每次都是新对象
  return (
    <AppContext.Provider value={{ usersetUserthemesetTheme }}>
      <Header />    {/* 只需要 user */}
      <Content />   {/* 只需要 theme */}
    </AppContext.Provider>
  );
}

// 结果:theme 变化时,Header 也会重渲染
// 即使 Header 根本不用 theme

真实的测试

我用 React DevTools Profiler 实际测试了:

  • 切换主题时,整个应用都重渲染
  • 即使某些组件只用了 user,跟 theme 完全无关

为什么?

因为 Context 的机制:只要 value 对象变了,所有消费者都重渲染。

// 每次渲染都创建新对象
const value = { user, setUser, theme, setTheme };
// 即使内容相同,引用不同,React 认为变了

解决方案对比

方案 1:拆分 Context(推荐)

// 环境:React 18+
// 场景:拆分成多个独立的 Context

const UserContext = createContext();
const ThemeContext = createContext();

function App() {
  const [user, setUser] = useState({ name: 'Alice' });
  const [theme, setTheme] = useState('light');
  
  const userValue = useMemo(() => ({ user, setUser }), [user]);
  const themeValue = useMemo(() => ({ theme, setTheme }), [theme]);
  
  return (
    <UserContext.Provider value={userValue}>
      <ThemeContext.Provider value={themeValue}>
        <Header />
        <Content />
      </ThemeContext.Provider>
    </UserContext.Provider>
  );
}

方案 2:用 Zustand 替代(推荐)

// 环境:React 18+ with zustand
// 场景:用状态管理库代替 Context

import { create } from 'zustand';

const useStore = create((set) => ({
  user: { name: 'Alice' },
  theme: 'light'setUser: (user) => set({ user }),
  setTheme: (theme) => set({ theme }),
}));

// Header 只订阅 user
function Header() {
  const user = useStore(state => state.user);
  // theme 变化时,Header 不会重渲染
  return <div>{user.name}</div>;
}

// Content 只订阅 theme
function Content() {
  const theme = useStore(state => state.theme);
  // user 变化时,Content 不会重渲染
  return <div className={theme}>Content</div>;
}

Zustand 的性能明显优于 Context,因为它支持细粒度订阅

我的经验教训

Context 适合:

  • 主题、语言设置(低频变化)
  • 用户信息(登录后基本不变)
  • 全局配置(几乎不变)

Context 不适合:

  • 表单输入状态(高频变化)
  • 动画状态(每帧都变)
  • 购物车数量(频繁更新)

给读者的建议:

  • 用 Context 前先问:这个数据变化频繁吗?
  • 如果 >1 次/秒,考虑 Zustand 或其他方案
  • 如果必须用 Context,记得用 useMemo 优化 value

认知 4:AI 擅长生成代码,但不擅长架构决策

写这个系列时的 AI 使用体验

AI 帮了什么:

  • ✅ 快速生成代码示例
  • ✅ 解释 API 用法
  • ✅ 提供多种实现方案
  • ✅ 帮我整理思路和大纲

AI 没帮上的:

  • ❌ 告诉我"该用哪个方案"
  • ❌ 解释"为什么这样设计"
  • ❌ 指出"这样写的隐患"
  • ❌ 提供适合具体场景的决策

具体案例

案例 1:表单库选择

我:帮我创建一个注册表单
AI:[生成了 Formik 的代码]

我:为什么用 Formik 而不是 React Hook Form?
AI:[罗列优缺点,但没有明确建议]

最后:我自己研究后选择了 React Hook Form

案例 2:状态管理选择

我:帮我管理购物车状态
AI:[有时生成 Redux,有时生成 Context,有时生成 Zustand]

我发现:AI 的选择是随机的,没有考虑项目规模

AI 代码的常见问题

问题 1:性能优化缺失

// AI 生成的 Context 代码
const value = { user, setUser, theme, setTheme };
// 没有 useMemo,每次渲染都创建新对象 ❌

// 需要手动优化
const value = useMemo(() => ({ user, setUser }), [user]);

问题 2:边界情况未处理

// AI 生成的异步验证
validate: async (value) => {
  const exists = await checkUsername(value);
  return exists ? 'Username already exists' : true;
}
// 没有防抖,每次输入都请求 ❌

// 需要手动添加防抖
import { debounce } from 'lodash';

const debouncedCheck = debounce(async (value) => {
  const exists = await checkUsername(value);
  return exists ? 'Username already exists' : true;
}, 500);

问题 3:错误处理简陋

// AI 生成的表单提交
const onSubmit = async (data) => {
  await submitForm(data);
};
// 没有 try-catch,没有 loading 状态 ❌

// 需要手动完善
const onSubmit = async (data) => {
  setLoading(true);
  try {
    await submitForm(data);
    toast.success('Submitted successfully');
  } catch (error) {
    toast.error(error.message);
  } finally {
    setLoading(false);
  }
};

我总结的"AI 协作最佳实践"

明确告诉 AI 项目背景:

❌ 不好:帮我创建状态管理

✅ 好:中型电商项目(20个组件),需要管理购物车和用户状态,用 Zustand 帮我写

分步骤验证:

  1. 让 AI 生成基础代码
  2. 自己审查性能、错误处理
  3. 让 AI 优化特定部分
  4. 再次审查和测试

建立检查清单:

  • 是否有性能问题?
  • 是否处理了错误?
  • 是否有内存泄漏?
  • 类型是否安全?
  • 边界情况是否考虑?

AI 的价值在哪里?

我的结论:

AI 是"代码生成器",不是"架构师"

AI 能做的:

  • 快速生成样板代码
  • 提供多种实现方式
  • 解释技术概念
  • 帮助学习新技术

人类需要做的:

  • 选择合适的方案
  • 优化性能和边界情况
  • 建立架构规范
  • 做出权衡决策

对读者的建议:

  • 不要盲目信任 AI 生成的代码
  • 把 AI 当作"初稿生成器"
  • 最终的质量由你把控
  • 保持批判性思维

认知 5:选择工具不是技术问题,是场景问题

系列开始前的想法

我以为:

学会 Redux 就能解决所有状态管理问题

写完这个系列,我意识到:

没有"最好的工具",只有"最合适的场景"

决策框架的建立

以前的思维:

遇到状态管理问题 → 用 Redux

现在的思维:

遇到问题 → 分析场景 → 选择方案

问自己:

  1. 数据的"真相源"在哪里?(客户端 or 服务端)
  2. 数据变化频率?(高频 or 低频)
  3. 使用范围?(单组件 or 全局)
  4. 项目规模?(小 or 大)
  5. 团队熟悉度?

我的场景 → 方案映射表

场景我的选择理由
服务端数据React Query专门处理缓存、同步、失效
全局 UI 状态Zustand简单、性能好、支持细粒度订阅
主题、语言Context低频变化,官方 API
表单React Hook Form性能优先,大表单友好
通知、埋点EventBus完全解耦,跨组件通信
复杂业务逻辑Redux Toolkit标准化流程,适合大团队

这个认知的价值

对我写代码的影响:

以前:

// 所有状态都用 Redux
const cartItems = useSelector(state => state.cart.items);
const user = useSelector(state => state.user);
const theme = useSelector(state => state.ui.theme);

现在:

// 根据场景选择工具
const { data: user } = useQuery(['user'], fetchUser);  // 服务端数据
const theme = useThemeStore(state => state.theme);      // 客户端 UI
const { data: cart } = useQuery(['cart'], fetchCart);  // 服务端数据

好处:

  • 代码更清晰(一眼看出数据来源)
  • 性能更好(各司其职)
  • 维护更容易(职责分离)

给读者的建议

建立自己的决策框架:

  1. 列出你常遇到的场景
  2. 记录你的选择和理由
  3. 定期回顾和优化

不要追求"大一统":

  • 一个项目用多种工具是正常的
  • 每种工具解决特定问题
  • 关键是职责分明

实用 > 完美:

  • 别陷入选择困难症
  • 先选一个,实践中调整
  • 技术债可以还,先把功能做出来

我还困惑的问题

写完这个系列,我仍然有很多困惑:

困惑 1:服务端组件(RSC)会如何改变状态管理?

  • Next.js 14 的 Server Components
  • 数据直接在服务端获取
  • 客户端状态会更少吗?
  • React Query 还需要吗?

困惑 2:Signals 是未来吗?

  • SolidJS、Preact Signals
  • 比 useState 更细粒度的更新
  • 会成为 React 的一部分吗?
  • 现在值得学习吗?

困惑 3:AI 会如何改变前端开发?

  • AI 生成代码越来越好
  • 我们还需要深入学习这些吗?
  • 还是应该专注于架构和业务?

困惑 4:Web3 应用的状态管理

  • 区块链数据的特殊性
  • 钱包连接状态
  • 与传统 Web 有何不同?

这些困惑很正常:

  • 技术在快速演进
  • 没有人能预知未来
  • 保持好奇心,持续学习

如果你对这些问题有想法,欢迎在评论区讨论。


系列回顾:这 9 篇文章的核心价值

这个系列不是教程,是思考记录。

核心价值不是"学会了什么工具",而是:

1. 建立思维框架

  • 数据的生命周期
  • 客户端状态 vs 服务端状态
  • 受控 vs 非受控的权衡
  • 场景驱动的技术选择

2. 理解设计权衡

  • 没有完美方案
  • 每个选择都有代价
  • 关键是匹配场景

3. 培养学习方法

  • 从问题出发,而非工具
  • 理解"为什么",而非"怎么做"
  • 实践 + 反思 + 输出

如果只记住 3 件事:

  1. 服务端数据 ≠ 客户端状态
  2. 技术选择 = 场景分析 + 权衡
  3. AI 是工具,架构靠思考

结语

写这个系列的初衷:

  • 整理自己不熟悉的知识点
  • 建立系统的认知框架
  • 分享学习过程和思考

意外的收获:

  • 不只是学会了工具
  • 更重要的是学会了"如何思考"
  • AI 的加入让学习效率提升,但也让我更清楚"人的价值"

对读者说:

如果这个系列对你有帮助,我很开心。

如果你有不同的看法,欢迎讨论。我的理解不一定对,保持开放心态。

状态管理是个大话题,我的探索才刚开始。

继续学习,持续思考。


推荐资源

官方文档(最权威)

优质博客

值得阅读的文章

  • "You Might Not Need Redux" - Dan Abramov
  • "Application State Management with React" - Kent C. Dodds
  • "Practical React Query" - TkDodo

实践项目推荐

  • 用 React Query 重构数据获取
  • 用 Zustand 做个 Todo App
  • 用 React Hook Form 做个复杂表单

这是状态管理系列的最后一篇。感谢你的阅读。