状态管理规范
组件开发规范
组件化开发是前端工程化的核心实践之一。良好的组件规范能提升代码复用率、降低维护成本。以下从设计原则、目录结构、props定义等方面展开说明。
组件设计原则
单一职责原则要求每个组件只做一件事。比如按钮组件只处理点击交互,不包含业务逻辑:
// Bad: 按钮混入业务逻辑
function OrderButton() {
const handleClick = () => {
fetch('/api/submit-order')
}
return <button onClick={handleClick}>提交订单</button>
}
// Good: 纯UI组件
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>
}
目录结构规范
推荐按功能划分的扁平化结构,避免过深嵌套:
components/
├── Form/
│ ├── Input/
│ │ ├── index.tsx
│ │ ├── style.module.css
│ │ └── types.ts
│ └── Select/
├── Feedback/
│ ├── Toast/
│ └── Modal/
└── Navigation/
├── Tabs/
└── Breadcrumb/
Props设计规范
使用TypeScript严格定义props类型,必填项需标记为required:
interface AvatarProps {
size: 'small' | 'medium' | 'large'
src: string
alt: string
shape?: 'circle' | 'square' // 可选参数
className?: string
}
function Avatar({ size, src, alt, shape = 'circle' }: AvatarProps) {
// 组件实现
}
样式隔离方案
推荐CSS Modules或Styled Components实现样式隔离:
/* Button.module.css */
.primary {
background: #1890ff;
&:hover {
background: #40a9ff;
}
}
import styles from './Button.module.css'
function Button({ type }) {
return (
<button className={styles[type]}>
{children}
</button>
)
}
状态管理规范
前端应用复杂度提升时,需要系统化的状态管理方案。以下从状态分类、管理策略、性能优化等维度说明。
状态分类标准
按作用域划分状态类型:
- 本地状态:组件内部使用,如input值
function Search() {
const [query, setQuery] = useState('')
return <input value={query} onChange={e => setQuery(e.target.value)} />
}
- 全局状态:跨组件共享,如用户信息
// store/user.ts
export const userStore = atom({
key: 'user',
default: null
})
// 组件中使用
function Avatar() {
const user = useRecoilValue(userStore)
return <img src={user.avatar} />
}
状态管理选型
根据场景选择合适方案:
方案 | 适用场景 | 示例 |
---|---|---|
Context API | 低频更新的全局状态 | 主题/语言切换 |
Redux | 复杂状态逻辑 | 购物车流程 |
MobX | 响应式编程场景 | 实时仪表盘 |
Recoil | 细粒度状态订阅 | 表单联动 |
状态更新规范
避免直接修改状态,遵循不可变原则:
// Bad
const [todos, setTodos] = useState([])
const handleComplete = (id) => {
todos.find(todo => todo.id === id).completed = true
setTodos(todos)
}
// Good
const handleComplete = (id) => {
setTodos(prev => prev.map(todo =>
todo.id === id ? { ...todo, completed: true } : todo
))
}
性能优化策略
大规模状态需考虑渲染优化:
- 使用选择器避免不必要的渲染
const completedTodos = selector({
key: 'completedTodos',
get: ({get}) => {
const todos = get(todoListState)
return todos.filter(todo => todo.completed)
}
})
- 批量更新状态
// React 18自动批处理
function handleClick() {
setName('Alice')
setAge(30) // 只会触发一次渲染
}
代码组织最佳实践
项目规模增长时,需要规范化的代码组织方式。
按功能划分模块
避免按技术类型分目录(如reducers/、components/),推荐按业务功能组织:
features/
├── auth/
│ ├── components/
│ ├── hooks/
│ └── store/
├── product/
│ ├── api/
│ ├── types/
│ └── utils/
└── order/
├── constants/
└── helpers/
自定义Hook规范
将复杂逻辑封装为Hook,命名以use开头:
function usePagination(initialPage = 1) {
const [page, setPage] = useState(initialPage)
const nextPage = () => setPage(p => p + 1)
const prevPage = () => setPage(p => Math.max(1, p - 1))
return { page, nextPage, prevPage }
}
// 使用示例
function UserList() {
const { page, nextPage } = usePagination()
// ...
}
类型定义管理
公共类型应集中定义,组件私有类型可放在组件目录:
// types/app.ts
export interface User {
id: string
name: string
roles: Array<'admin' | 'editor'>
}
// components/UserCard/types.ts
export interface UserCardProps {
user: User
showContact?: boolean
}
异常处理机制
健壮的应用需要统一的错误处理方案。
组件错误边界
使用React Error Boundary捕获组件错误:
class ErrorBoundary extends React.Component {
state = { hasError: false }
static getDerivedStateFromError() {
return { hasError: true }
}
componentDidCatch(error, info) {
logErrorToService(error, info)
}
render() {
if (this.state.hasError) {
return <FallbackUI />
}
return this.props.children
}
}
异步操作处理
规范loading/error状态管理:
function useAsync(asyncFn) {
const [state, setState] = useState({
loading: false,
error: null,
data: null
})
const execute = useCallback(() => {
setState({ loading: true, error: null, data: null })
return asyncFn()
.then(data => setState({ loading: false, error: null, data }))
.catch(error => setState({ loading: false, error, data: null }))
}, [asyncFn])
return { ...state, execute }
}
状态回滚机制
操作失败时应恢复之前状态:
function useUndoState(initialValue) {
const [states, setStates] = useState([initialValue])
const [index, setIndex] = useState(0)
const current = states[index]
const setCurrent = (newValue) => {
setStates(prev => [...prev.slice(0, index + 1), newValue])
setIndex(prev => prev + 1)
}
const undo = () => index > 0 && setIndex(prev => prev - 1)
const redo = () => index < states.length - 1 && setIndex(prev => prev + 1)
return [current, setCurrent, { undo, redo }]
}
测试规范要求
完善的测试是质量保障的重要环节。
组件测试要点
使用Testing Library编写组件测试:
import { render, screen, fireEvent } from '@testing-library/react'
test('Button click triggers callback', () => {
const handleClick = jest.fn()
render(<Button onClick={handleClick}>OK</Button>)
fireEvent.click(screen.getByText('OK'))
expect(handleClick).toHaveBeenCalledTimes(1)
})
状态测试策略
验证状态管理逻辑的正确性:
test('todo reducer adds new item', () => {
const initialState = { todos: [] }
const action = { type: 'ADD_TODO', payload: 'Learn React' }
const newState = todoReducer(initialState, action)
expect(newState.todos).toHaveLength(1)
expect(newState.todos[0].text).toBe('Learn React')
})
E2E测试规范
关键业务流程需添加端到端测试:
describe('Checkout Flow', () => {
it('should complete purchase', () => {
cy.visit('/products')
cy.get('[data-testid="product-1"]').click()
cy.contains('Add to Cart').click()
cy.visit('/cart')
cy.contains('Checkout').click()
cy.url().should('include', '/checkout/success')
})
})
本站部分内容来自互联网,一切版权均归源网站或源作者所有。
如果侵犯了你的权益请来信告知我们删除。邮箱:cc@cccx.cn