RN Hooks 设计规范与反模式清单

@[toc]

如果你写 RN 写到后面,开始出现下面这些情况:

  • 一个页面引用 5~8 个自定义 Hook
  • 一个 Hook 内部 300 行,还不敢拆
  • useEffect 嵌套 useEffect,依赖数组随缘写
  • 改一个状态,引发一连串莫名其妙的更新

那问题往往不在 React Native,而在 Hooks 的设计方式本身已经失控了

这篇文章不讲"Hook 是什么",而是讲:
在真实 RN 项目里,Hooks 应该怎么设计,哪些写法一定会把项目带沟里。

一、为什么 RN 项目里的 Hook 特别容易"写炸"?

先说一个现实结论:

Hook 是"状态和行为的放大器",不是解药。

1. Hook 太容易承载"过多职责"

很多项目里的 Hook,最后都会长成这样:

ts 复制代码
function usePageLogic() {
  // 请求
  // 权限判断
  // 数据转换
  // 埋点
  // 页面跳转
}

写的时候很爽,

三个月后------没人敢改。

2. useEffect 本身就是"隐性依赖地狱"

ts 复制代码
useEffect(() => {
  doSomething(a, b, c)
}, [a, b])
  • 少写依赖 → 逻辑不一致
  • 多写依赖 → 无限触发
  • eslint 一关 → 世界和平(假的)

3. RN 的异步 + 生命周期放大问题

  • 页面切后台
  • 组件卸载
  • 网络请求返回

Hook 如果没设计好,很容易:

  • setState on unmounted component
  • 重复请求
  • 内存泄漏

二、一个健康 Hook 的设计目标

在进入规范前,先给你一个判断标准。

一个"设计良好"的 Hook,应该满足:

  1. 职责单一
  2. 可预测
  3. 可组合
  4. 不隐藏业务规则

如果一个 Hook 做不到这四点,迟早要拆。

三、Hook 设计的 5 条核心规范

规范 1:Hook 只做"状态编排",不做业务裁决

错误示例:

ts 复制代码
function useUserPermission(user) {
  if (user.role === 'admin') {
    // ...
  }
}

正确做法:

ts 复制代码
// domain/user.ts
export function canEdit(user: User) {
  return user.role === 'admin'
}

// hooks/useUserPermission.ts
export function useUserPermission(user: User) {
  return canEdit(user)
}

判断规则属于 domain,不属于 hook。

规范 2:一个 Hook 只管理一类状态

反例(很常见):

ts 复制代码
function usePage() {
  const [list, setList] = useState([])
  const [loading, setLoading] = useState(false)
  const [selected, setSelected] = useState(null)
}

正确拆法:

ts 复制代码
useListData()
useLoadingState()
useSelection()

Hook 是组合单位,不是容器。

规范 3:不要在 Hook 里偷偷"改全局"

危险写法:

ts 复制代码
function useInit() {
  useEffect(() => {
    store.setState(...)
  }, [])
}

问题是:

  • 页面一 mount 就改全局
  • 调用方完全无感知

更好的方式是:

ts 复制代码
function useInit() {
  return () => {
    store.setState(...)
  }
}

副作用必须显式触发。

规范 4:异步 Hook 必须考虑"取消和卸载"

错误示例:

ts 复制代码
useEffect(() => {
  fetchData().then(setData)
}, [])

改进版本:

ts 复制代码
useEffect(() => {
  let mounted = true

  fetchData().then(res => {
    if (mounted) setData(res)
  })

  return () => {
    mounted = false
  }
}, [])

这是 RN 项目里非常真实的坑。

规范 5:Hook 返回值要"语义清晰"

不要这样:

ts 复制代码
const [a, b, c] = useSomething()

推荐这样:

ts 复制代码
const {
  data,
  loading,
  refresh
} = useSomething()

Hook 是接口,不是数组谜题。

四、useEffect 的正确使用姿势

一句话总结:

能不用 useEffect,就不用。

1. 能同步算出来的,不要进 effect

错误:

ts 复制代码
useEffect(() => {
  setTotal(price * count)
}, [price, count])

正确:

ts 复制代码
const total = price * count

2. 派生状态 > effect 驱动状态

ts 复制代码
const filteredList = useMemo(() => {
  return list.filter(...)
}, [list])

3. effect 只做三件事

  • 网络请求
  • 订阅 / 监听
  • 与外部系统交互

其他情况,大概率是设计问题。

五、常见 Hook 反模式清单(重点)

反模式 1:巨型 Hook

ts 复制代码
usePageLogic() // 400 行

症状:

  • 无法复用
  • 无法测试
  • 无法拆

反模式 2:useEffect 充当"状态中转站"

ts 复制代码
useEffect(() => {
  setB(calcA(a))
}, [a])

这基本等同于:

  • 手写响应式系统
  • 极易失控

反模式 3:Hook 内部偷偷导航

ts 复制代码
navigation.navigate(...)

页面不知道:

  • 什么时候跳
  • 为什么跳

反模式 4:Hook 强依赖页面结构

ts 复制代码
useScrollPosition(ref)

ref 来自页面,生命周期复杂,极易出问题。

六、一个可运行的 Demo:拆解"失控 Hook"

原始写法

ts 复制代码
function useProfilePage() {
  const [user, setUser] = useState(null)
  const [loading, setLoading] = useState(false)

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

  return { user, loading }
}

拆解后

ts 复制代码
function useUserData() {
  const [user, setUser] = useState(null)

  useEffect(() => {
    fetchUser().then(setUser)
  }, [])

  return user
}

function useLoading(initial = false) {
  const [loading, setLoading] = useState(initial)
  return { loading, setLoading }
}

页面组合:

ts 复制代码
const user = useUserData()
const { loading } = useLoading()

逻辑更清晰,也更容易复用。

七、真实项目里的变化

在一个中大型 RN 项目中,重构 Hooks 后:

  • useEffect 数量 ↓ 40%+
  • 页面逻辑可读性明显提升
  • 新人调试 Hook 成本大幅降低
  • "一改就炸"的情况明显减少

最后的总结

如果你记住这三点就够了:

  1. Hook 不是垃圾桶
  2. 业务规则永远不属于 Hook
  3. useEffect 是最后手段,不是默认选择

Hook 写得好,RN 项目会非常优雅;

Hook 写得乱,TS、Redux、架构都救不了你。

相关推荐
你真是饿了1 天前
1.C++入门基础
开发语言·c++
天天进步20151 天前
Python全栈项目:实时数据处理平台
开发语言·python
Tipriest_1 天前
Python中is关键字详细说明,比较的是地址还是值
开发语言·python
sheji34161 天前
【开题答辩全过程】以 基于Python的餐饮统计系统的设计和实 现为例,包含答辩的问题和答案
开发语言·python
elseif1231 天前
【C++】并查集&家谱树
开发语言·数据结构·c++·算法·图论
catchadmin1 天前
2026 年 PHP 前后端分离后台管理系统推荐 企业级方案
开发语言·php
小李独爱秋1 天前
计算机网络经典问题透视:蜂窝网络切换如何“扼杀”你的TCP连接?
网络·网络协议·tcp/ip·计算机网络·php·信息与通信
LGL6030A1 天前
Java学习历程26——线程安全
java·开发语言·学习
遨游xyz1 天前
排序-快速排序
开发语言·python·排序算法
傻啦嘿哟1 天前
Python操作PDF页面详解:删除指定页的完整方案
开发语言·python·pdf