在这里插入图片描述
在这里插入图片描述

子玥酱 (掘金 / 知乎 / CSDN / 简书 同名)

大家好,我是 子玥酱,一名长期深耕在一线的前端程序媛 👩‍💻。曾就职于多家知名互联网大厂,目前在某国企负责前端软件研发相关工作,主要聚焦于业务型系统的工程化建设与长期维护。

我持续输出和沉淀前端领域的实战经验,日常关注并分享的技术方向包括 前端工程化、小程序、React / RN、Flutter、跨端方案
在复杂业务落地、组件抽象、性能优化以及多端协作方面积累了大量真实项目经验。

技术方向:前端 / 跨端 / 小程序 / 移动端工程化
内容平台:
掘金、知乎、CSDN、简书
创作特点:
实战导向、源码拆解、少空谈多落地
文章状态:
长期稳定更新,大量原创输出

我的内容主要围绕 前端技术实战、真实业务踩坑总结、框架与方案选型思考、行业趋势解读 展开。文章不会停留在“API 怎么用”,而是更关注为什么这么设计、在什么场景下容易踩坑、真实项目中如何取舍,希望能帮你在实际工作中少走弯路。

子玥酱 · 前端成长记录官 ✨
👋 如果你正在做前端,或准备长期走前端这条路
📚 关注我,第一时间获取前端行业趋势与实践总结
🎁 可领取 11 类前端进阶学习资源(工程化 / 框架 / 跨端 / 面试 / 架构)
💡 一起把技术学“明白”,也用“到位”

持续写作,持续进阶。
愿我们都能在代码和生活里,走得更稳一点 🌱

引言

如果你在 RN 项目里工作过 1 年以上,大概率经历过一个节点:

大家都知道状态设计不对了,但没人敢动。

于是会议室里开始出现这种话:

  • 「这个状态改动有点大」
  • 「要不等下一个大版本?」
  • 「顺手重构风险太高」

最后的结局往往是:

等到某个业务节点,直接开一个新分支,大版本重写。

这并不是 RN 团队“懒”,而是 RN 的状态模型,本身就不擅长中途修正方向

RN 的状态问题,往往不是“写错”,而是“写早了”

先说一个很典型的 RN 初期状态结构。

function UserPage() {
  const [loading, setLoading] = useState(false);
  const [user, setUser] = useState<User | null>(null);

  useEffect(() => {
    setLoading(true);
    fetchUser().then(data => {
      setUser(data);
      setLoading(false);
    });
  }, []);

  if (loading) return <Loading />;
  if (!user) return null;

  return <UserView user={user} />;
}

这段代码在 早期几乎无可挑剔

  • useState 很自然
  • useEffect 很清晰
  • 状态和 UI 强一致

问题从来不是这里开始的。

当“复用需求”出现,状态开始被整体抬升

很快你会遇到:

  • 多个页面需要用户信息
  • 某些操作需要刷新用户
  • 登录态需要全局感知

于是你做了 RN 项目里最合理、也最危险的一步

把状态提到全局。

// store/user.ts
const initialState = {
  user: null,
  loading: false,
};

export function userReducer(state = initialState, action) {
  switch (action.type) {
    case 'FETCH_USER':
      return { ...state, loading: true };
    case 'SET_USER':
      return { user: action.payload, loading: false };
    default:
      return state;
  }
}

然后组件变成:

const user = useSelector(state => state.user.user);
const loading = useSelector(state => state.user.loading);

到这里为止,一切看起来仍然“非常专业”。

真正的问题:状态被 UI 消费方式锁死了

RN 状态重构困难的核心原因只有一句话:

组件函数,已经深度绑定了状态结构本身。

看一个真实后期常见的写法:

const {
  user,
  loading,
  error,
  permissions,
  profileComplete,
} = useSelector(state => state.user);

你注意到没有:

  • UI 不只是“使用状态”
  • UI 假设了状态的形态
  • render 逻辑已经写死了字段结构

这意味着什么?意味着你现在想做哪怕一个“看起来不大的重构”,比如:

  • permissions 拆到 auth 模块
  • profileComplete 变成派生状态
  • loading 改成按请求维度拆分

你改的不是一个文件,而是:

  • 多个页面
  • 多个 hooks
  • 多条渲染路径

为什么“顺手重构”在 RN 中几乎不存在

在很多技术栈里,我们习惯:

改一点 → 验证 → 再改一点

但在 RN 的状态系统里,这个节奏很难成立

原因很现实:

状态改动 ≈ render 路径改动

if (loading && !user) return <Skeleton />;
if (error) return <Error />;

一旦状态语义发生变化:

  • loading 不再是 boolean
  • error 不再全局唯一

UI 判断条件就必须一起改。

hooks 放大了状态依赖面

useEffect(() => {
  if (user) {
    track(user.id);
  }
}, [user]);

状态字段一拆:

  • effect 依赖要改
  • 副作用触发时机要重新审视

这不是“改类型”,而是“改行为”。

Redux / Zustand / MobX,都逃不开这个结局

很多人会说:

那是 Redux 的问题,用 Zustand 会好点。

但现实是:

const useUserStore = create(set => ({
  user: null,
  setUser: (user) => set({ user }),
}));

组件里依然是:

const user = useUserStore(state => state.user);

只要组件直接消费状态形态,重构成本就已经确定了。

工具只能改变语法,
改变不了“UI 依赖状态结构”这个事实。

为什么最终只能“大版本重写”

当 RN 项目走到后期,团队通常会发现:

  • 改一个状态,测试成本极高
  • UI 行为不可预期
  • 旧逻辑和新逻辑交织

这时有两条路:

第一条:持续打补丁

  • 加字段
  • 做兼容
  • 写 if else

结果是:

  • reducer 越来越像历史博物馆
  • 新人不敢删代码
  • 状态语义逐渐失真

第二条:冻结现状,重写一套

  • 新状态模型
  • 新页面结构
  • 新数据流

这条路看起来激进,但风险反而更可控。

因为你终于可以:

  • 不再背负旧状态假设
  • 重画状态边界
  • 一次性清理依赖关系

对 Flutter 的一个重要预警

如果你正在写 Flutter,这一切其实非常熟悉。

final user = ref.watch(userProvider);
if (user.loading) ...
if (user.error != null) ...

一模一样。

Flutter 的 Widget tree,
就是 RN render function 的另一种形态。

区别只是:

  • RN 是函数
  • Flutter 是 build

状态 → UI 结构绑定这件事,完全一致。

真正降低“重构必须重写”的唯一方式

不在于选什么库,而在于一个原则:

UI 不直接依赖“状态结构”,而依赖“语义结果”。

举个例子。

错误方式(结构依赖)

const { loading, error, user } = state.user;

更可控的方式(语义依赖)

const userViewState = selectUserViewState(state);
function selectUserViewState(state) {
  if (state.user.loading) return 'loading';
  if (state.user.error) return 'error';
  if (!state.user.user) return 'empty';
  return 'ready';
}

UI 只关心:

switch (viewState) {
  case 'loading':
  case 'error':
}

Flutter 里也是同理。

总结

RN 状态重构之所以常常演变成“大版本重写”,不是因为团队不专业,而是因为:

  • 状态结构被 UI 消费得太早
  • render 逻辑绑定了状态形态
  • 中途修正会牵一发动全身

当你理解这一点,就会明白:

重写,往往不是失败,而是唯一理性的止损方式。

而这,也是 Flutter 团队今天就该警惕的未来。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐