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、架构都救不了你。

相关推荐
HyperAI超神经3 小时前
【vLLM 学习】vLLM TPU 分析
开发语言·人工智能·python·学习·大语言模型·vllm·gpu编程
ForteScarlet4 小时前
如何解决 Kotlin/Native 在 Windows 下 main 函数的 args 乱码?
开发语言·windows·kotlin
月殇_木言4 小时前
应用层自定义协议与序列化
开发语言
a努力。4 小时前
网易Java面试被问:偏向锁在什么场景下反而降低性能?如何关闭?
java·开发语言·后端·面试·架构·c#
前端达人4 小时前
CSS终于不再是痛点:2026年这7个特性让你删掉一半JavaScript
开发语言·前端·javascript·css·ecmascript
wjs20244 小时前
SVG 多边形
开发语言
H_-H4 小时前
值返回与引用返回(c++)
开发语言·c++
csbysj20204 小时前
Java 日期时间处理详解
开发语言
我命由我123454 小时前
Python Flask 开发 - Flask 快速上手(Flask 最简单的案例、Flask 处理跨域、Flask 基础接口)
服务器·开发语言·后端·python·学习·flask·学习方法