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 就能实现的方法?”
决策树
- 需要响应用户交互? 使用事件处理函数
- 需要从 props/state 计算值? 在渲染期间计算
- 需要缓存昂贵的计算? 使用
useMemo - 需要在 prop 变化时重置状态? 使用
keyprop - 需要与外部系统同步? 使用带清理的 Effect
- 需要在 Effect 中使用非响应式代码? 使用
useEffectEvent - 需要不会触发渲染的可变值? 使用 ref
何时使用 Effect
与外部系统同步:浏览器 API(WebSocket、IntersectionObserver)、第三方非 React 库、window/document 事件监听器、非 React DOM 元素(视频、地图)。
何时不使用 Effect
- 派生状态——在渲染期间计算
- 昂贵计算——使用
useMemo - 在 prop 变化时重置状态——使用
keyprop - 响应用户事件——使用事件处理函数
- 通知父组件状态变化——在同一个事件处理函数中更新两者
- Effect 链——计算派生状态并在一个事件处理函数中更新
Refs
- 用于不影响渲染的值(定时器 ID、DOM 节点引用)
- 绝不在渲染期间读取或写入
ref.current;仅在事件处理函数和 Effect 中操作 - 对于动态列表,使用 ref 回调(而不是循环中的
useRef) - 使用
useImperativeHandle限制父组件可访问的内容
自定义 Hook
- 共享逻辑,而非状态——每次调用获得独立的状态实例
- 仅当实际调用其他 Hook 时才命名为
useXxx;否则使用普通函数 - 避免生命周期 Hook(
useMount、useEffectOnce)——直接使用useEffect,以便 linter 捕获缺失的依赖 - 专注于单一具体用例
组件模式
- 受控:父组件拥有状态;非受控:组件自身拥有状态
- 优先使用
children组合而非 prop 逐层传递 - 将切换大型组件树的布尔 prop(
isEditing、isThread、hideAttachments)视为组合的坏味道;对于不同的用例,优先使用独立的组合组件 - 对于复杂的可复用 UI,优先使用带 provider 作用域状态/动作的组合组件,而非带有许多可选 prop 的单一组件
- 对于作用域组件族以及真正的全局状态,使用 Context,当它定义了后代消费的局部接口时
- 直接在 JSX 中渲染以实现 UI 变化;避免使用配置数组的迷你框架,除非配置是真实的领域数据
- 当兄弟或外部控件需要访问相同状态/动作时,提升 provider 边界
- 当需要在状态更新后同步读取 DOM 时,使用
flushSync
有关代码示例和详细模式,请参阅 react-patterns.md。






