跳到正文

React Hooks 与副作用 ​

  • 状态:未准备
  • 适用方向:前端 / Go 全栈
  • 最近复习:待填写
  • 下次复习:待安排

一句话回答 ​

useState 保存影响视图的数据,useRef 保存跨渲染但不直接驱动视图的数据,useEffect 负责与外部系统同步,并在依赖改变或卸载时清理旧同步。

原理解释 ​

Effect 什么时候执行? ​

Effect 在组件提交后运行;依赖通过 Object.is 比较。没有依赖数组时每次相关提交后运行,空数组不会因普通依赖变化重跑,但不代表整个应用生命周期绝对只执行一次。依赖变化时,先清理旧 Effect,再建立新 Effect;卸载时也清理。

jsx
useEffect(() => {
  const controller = new AbortController()
  let active = true
  async function load() {
    try {
      const response = await fetch(`/api/users/${id}`, {
        signal: controller.signal,
      })
      if (!response.ok) throw new Error(`HTTP ${response.status}`)
      const data = await response.json()
      if (active) setUser(data)
    } catch (error) {
      if (active) setError(String(error))
    }
  }
  load()
  return () => {
    active = false
    controller.abort()
  }
}, [id])

示例重点是取消与过期结果隔离;实际页面还需在切换时重置 loading/error,并处理数据缓存和运行时校验。不能把 Effect 回调本身写成 async,因为它需要返回清理函数或 undefined。

陈旧闭包怎么处理? ​

先判断逻辑是否属于事件还是外部同步。需要同步变化的响应式值应进入依赖;基于前值更新可用函数式 setter;不驱动 UI 的连接句柄、计时器 ID 可放 ref。不能为了消除 lint 提示随意删依赖。

场景与边界 ​

能在渲染中直接计算的派生值,通常不需要 Effect 再写一次 state。useLayoutEffect 用于必须在绘制前测量或调整布局的场景,会阻塞绘制,不能当默认替代。修改 ref.current 不触发重新渲染。

项目中的应用 ​

结合 SimulSpeak 媒体与窗口清理 和 Blog 动画,检查监听、rAF、连接、媒体轨道是否有对应释放,异步晚到资源是否再次检查会话是否有效。

常见追问 ​

  • Hooks 为什么不能随意放条件里? useState/useEffect 等依靠稳定调用顺序关联状态;应在组件或自定义 Hook 顶层调用。特殊 API use 有单独规则,不能泛化。
  • 自定义 Hook 会共享状态吗? 默认共享逻辑,每次调用各有状态;真正共享需外部 store、Context 等机制。
  • 空数组加 ref 是万能修复吗? 不是,可能使外部系统不随真正依赖变化重建,应先明确生命周期。

参考资料 ​

理解机制,讲清取舍,用复盘检验掌握。