react-best-practices

react-best-practices

在读取或编写 React 组件(带有 React 导入的 .tsx、.jsx 文件)时使用。

51Star
6Fork
更新于 2026/6/24
SKILL.md
readonly只读
name
react-best-practices
description

在读取或编写 React 组件(带有 React 导入的 .tsx、.jsx 文件)时使用。

React 最佳实践

与 TypeScript 配合使用

在处理 React 时,始终同时加载此技能和 typescript-best-practices。TypeScript 模式(类型优先开发、可辨识联合、Zod 验证)适用于 React 代码。

核心原则:Effect 是逃生舱

Effect 让你“跳出”React 与外部系统同步。大多数组件逻辑不应使用 Effect。 在编写 Effect 之前,问自己:“有没有不用 Effect 就能实现的方法?”

决策树

  1. 需要响应用户交互? 使用事件处理函数
  2. 需要从 props/state 计算值? 在渲染期间计算
  3. 需要缓存昂贵的计算? 使用 useMemo
  4. 需要在 prop 变化时重置状态? 使用 key prop
  5. 需要与外部系统同步? 使用带清理的 Effect
  6. 需要在 Effect 中使用非响应式代码? 使用 useEffectEvent
  7. 需要不会触发渲染的可变值? 使用 ref

何时使用 Effect

外部系统同步:浏览器 API(WebSocket、IntersectionObserver)、第三方非 React 库、window/document 事件监听器、非 React DOM 元素(视频、地图)。

何时不使用 Effect

  • 派生状态——在渲染期间计算
  • 昂贵计算——使用 useMemo
  • 在 prop 变化时重置状态——使用 key prop
  • 响应用户事件——使用事件处理函数
  • 通知父组件状态变化——在同一个事件处理函数中更新两者
  • Effect 链——计算派生状态并在一个事件处理函数中更新

Refs

  • 用于不影响渲染的值(定时器 ID、DOM 节点引用)
  • 绝不在渲染期间读取或写入 ref.current;仅在事件处理函数和 Effect 中操作
  • 对于动态列表,使用 ref 回调(而不是循环中的 useRef
  • 使用 useImperativeHandle 限制父组件可访问的内容

自定义 Hook

  • 共享逻辑,而非状态——每次调用获得独立的状态实例
  • 仅当实际调用其他 Hook 时才命名为 useXxx;否则使用普通函数
  • 避免生命周期 Hook(useMountuseEffectOnce)——直接使用 useEffect,以便 linter 捕获缺失的依赖
  • 专注于单一具体用例

组件模式

  • 受控:父组件拥有状态;非受控:组件自身拥有状态
  • 优先使用 children 组合而非 prop 逐层传递
  • 将切换大型组件树的布尔 prop(isEditingisThreadhideAttachments)视为组合的坏味道;对于不同的用例,优先使用独立的组合组件
  • 对于复杂的可复用 UI,优先使用带 provider 作用域状态/动作的组合组件,而非带有许多可选 prop 的单一组件
  • 对于作用域组件族以及真正的全局状态,使用 Context,当它定义了后代消费的局部接口时
  • 直接在 JSX 中渲染以实现 UI 变化;避免使用配置数组的迷你框架,除非配置是真实的领域数据
  • 当兄弟或外部控件需要访问相同状态/动作时,提升 provider 边界
  • 当需要在状态更新后同步读取 DOM 时,使用 flushSync

有关代码示例和详细模式,请参阅 react-patterns.md