--- 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` 包获取应用组件 ## 最佳实践 - 禁止循环导入 - 使用适当的导入/导出 - 遵循既定的导入顺序 - 组合组件而非扩展组件 - 保持组件专注于单一职责 - 用清晰的注释记录复杂逻辑 - 遵循项目的文件夹结构和命名约定 - 优先使用受控组件而非非受控组件