| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128 |
- ---
- description: LabelStudio 客户端应用的 React 编码标准和最佳实践
- globs: **/*.jsx,**/*.tsx
- ---
- # React 最佳实践
- ## 项目结构
- - 所有前端代码位于 `web` 目录
- - 主应用代码位于 `web/apps/labelstudio`
- - 共享库位于 `web/libs`
- - 遵循既定的目录结构:
- - `components/`: 可复用的 UI 组件
- - `pages/`: 顶层页面组件
- - `utils/`: 工具函数
- - `hooks/`: 自定义 React Hooks
- - `atoms/`: Jotai 原子状态定义
- - `providers/`: Context 提供者
- - `services/`: API 和其他服务
- - `types/`: TypeScript 类型定义
- - `assets/`: 静态资源
- ## 组件结构
- - 使用函数组件而非类组件
- - 保持组件小而专注
- - 将可复用逻辑提取到自定义 Hooks
- - 使用组合而非继承
- - 使用 TypeScript 实现适当的 prop 类型
- - 将大组件拆分为更小、更专注的组件
- - 遵循一致的文件组织模式:
- ```
- component-name/
- component-name.tsx
- component-name.module.scss
- component-name.test.tsx
- index.ts
- ```
- ## Hooks
- - 遵循 Hooks 规则
- - 使用自定义 Hooks 实现可复用逻辑
- - 保持 Hooks 专注和简单
- - 除非绝对必要,否则避免使用 useEffect
- - 在 useEffect 中使用适当的依赖数组
- - 在 useEffect 中实现清理逻辑
- - 避免嵌套 Hooks
- ## 状态管理
- - 使用 useState 管理本地组件状态
- - 使用 Jotai 原子而非 Context API 管理共享状态
- - 使用 atomWithReducer 处理复杂状态逻辑
- - 使用 atomWithQuery 处理任何 API 数据请求
- - 将状态保持在尽可能接近使用位置的地方
- - 通过适当的状态管理避免 prop drilling
- - 仅使用 Jotai 作为全局状态管理的单一数据源
- ## 性能
- - 实现适当的记忆化(useMemo, useCallback)
- - 对昂贵的组件使用 React.memo
- - 避免不必要的重渲染
- - 实现适当的懒加载
- - 在列表中使用适当的 key props
- - 分析和优化渲染性能
- ## 工具
- - 使用 Biome 进行代码检查和格式化
- - 遵循 .stylelintrc.json 中定义的 CSS/SCSS 检查规则
- - 使用 TypeScript 确保类型安全
- - 通过监控导入来控制打包大小
- ## 表单
- - 对表单输入使用受控组件
- - 实现适当的表单验证
- - 正确处理表单提交状态
- - 显示适当的加载和错误状态
- - 对复杂表单使用表单库
- - 为表单实现适当的无障碍功能
- ## 错误处理
- - 实现错误边界
- - 正确处理异步错误
- - 显示用户友好的错误消息
- - 实现适当的后备 UI
- - 适当记录错误
- - 优雅处理边缘情况
- ## 测试
- - 为组件编写单元测试
- - 为复杂流程实现集成测试
- - 使用 React Testing Library
- - 测试用户交互
- - 测试错误场景
- - 实现适当的模拟数据
- ## 无障碍性
- - 确保组件符合 WCAG 2.1 AA 标准
- - 使用语义化 HTML 元素
- - 实现适当的 ARIA 属性
- - 确保键盘导航
- - 使用屏幕阅读器测试
- - 处理焦点管理
- - 为图片提供适当的 alt 文本
- ## 代码组织
- - 使用适当的文件命名约定,采用 kebab-case,例如 ListItem -> `list-item.tsx`
- - 优先每个文件夹一个组件,但必要时将相关组件分组在一起,确保每个文件只有一个组件
- - 组件文件夹应包含一个 SCSS `.module.scss` 文件,文件名为组件的 kebab-case 形式,例如 ListItem -> `list-item.module.scss`
- - 实现适当的目录结构
- - UI 组件位于 `web/libs/ui`
- - 跨应用共享的应用组件(如某些页面级块)位于 `web/libs/app-common`
- - `web/apps` 中的代码只能从 `web/libs` 导入,`web/libs` 不能从 `web/apps` 导入
- - `web/libs/app-common` 中的代码只能从其他 `web/libs` 或 `web/apps` 导入。其他 `web/libs` 不能从 `web/libs/app-common` 导入
- - 将原子保存在全局 atoms 文件夹中,文件名与实体或状态意图匹配
- - 通过将 story 文件与组件文件放在一起,将所有组件及其状态添加到 Storybook,例如 `list-item.stories.tsx`
- - 使用 `@humansignal/ui` 包获取 UI 组件
- - 使用 `@humansignal/icons` 包获取图标
- - 使用 `@humansignal/core` 包获取核心工具/函数
- - 使用 `@humansignal/app-common` 包获取应用组件
- ## 最佳实践
- - 禁止循环导入
- - 使用适当的导入/导出
- - 遵循既定的导入顺序
- - 组合组件而非扩展组件
- - 保持组件专注于单一职责
- - 用清晰的注释记录复杂逻辑
- - 遵循项目的文件夹结构和命名约定
- - 优先使用受控组件而非非受控组件
|