State management specification
Component Development Standards
Component-based development is a core practice of front-end engineering. Good component standards can improve code reusability and reduce maintenance costs. The following elaborates on design principles, directory structure, props definition, and more.
Component Design Principles
The Single Responsibility Principle requires each component to do only one thing. For example, a button component should only handle click interactions without containing business logic:
// Bad: Button mixed with business logic
function OrderButton() {
const handleClick = () => {
fetch('/api/submit-order')
}
return <button onClick={handleClick}>Submit Order</button>
}
// Good: Pure UI component
function Button({ onClick, children }) {
return <button onClick={onClick}>{children}</button>
}
Directory Structure Standards
A flat structure organized by functionality is recommended, avoiding excessive nesting:
components/
├── Form/
│ ├── Input/
│ │ ├── index.tsx
│ │ ├── style.module.css
│ │ └── types.ts
│ └── Select/
├── Feedback/
│ ├── Toast/
│ └── Modal/
└── Navigation/
├── Tabs/
└── Breadcrumb/
Props Design Standards
Use TypeScript to strictly define props types, with required fields marked as required:
interface AvatarProps {
size: 'small' | 'medium' | 'large'
src: string
alt: string
shape?: 'circle' | 'square' // Optional parameter
className?: string
}
function Avatar({ size, src, alt, shape = 'circle' }: AvatarProps) {
// Component implementation
}
Style Isolation Solutions
CSS Modules or Styled Components are recommended for style isolation:
/* 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>
)
}
State Management Standards
As front-end applications grow in complexity, systematic state management solutions are needed. The following explains state classification, management strategies, and performance optimization.
State Classification Criteria
Classify state types by scope:
- Local State: Used within a component, such as input value
function Search() {
const [query, setQuery] = useState('')
return <input value={query} onChange={e => setQuery(e.target.value)} />
}
- Global State: Shared across components, such as user information
// store/user.ts
export const userStore = atom({
key: 'user',
default: null
})
// Usage in components
function Avatar() {
const user = useRecoilValue(userStore)
return <img src={user.avatar} />
}
State Management Selection
Choose the appropriate solution based on the scenario:
Solution | Suitable Scenarios | Example |
---|---|---|
Context API | Global state with low update frequency | Theme/language switching |
Redux | Complex state logic | Shopping cart flow |
MobX | Reactive programming scenarios | Real-time dashboard |
Recoil | Fine-grained state subscriptions | Form linkage |
State Update Standards
Avoid directly modifying state; follow the immutability principle:
// 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
))
}
Performance Optimization Strategies
For large-scale state, consider rendering optimizations:
- Use selectors to avoid unnecessary renders
const completedTodos = selector({
key: 'completedTodos',
get: ({get}) => {
const todos = get(todoListState)
return todos.filter(todo => todo.completed)
}
})
- Batch state updates
// React 18 automatic batching
function handleClick() {
setName('Alice')
setAge(30) // Only triggers one render
}
Code Organization Best Practices
As project size grows, standardized code organization is needed.
Organize by Feature
Avoid organizing by technical type (e.g., reducers/, components/); instead, organize by business functionality:
features/
├── auth/
│ ├── components/
│ ├── hooks/
│ └── store/
├── product/
│ ├── api/
│ ├── types/
│ └── utils/
└── order/
├── constants/
└── helpers/
Custom Hook Standards
Encapsulate complex logic into Hooks, prefixed with 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 }
}
// Usage example
function UserList() {
const { page, nextPage } = usePagination()
// ...
}
Type Definition Management
Public types should be centrally defined, while component-private types can reside in the component directory:
// 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
}
Exception Handling Mechanisms
Robust applications require unified error handling solutions.
Component Error Boundaries
Use React Error Boundary to catch component errors:
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
}
}
Asynchronous Operation Handling
Standardize loading/error state management:
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 }
}
State Rollback Mechanism
Revert to the previous state on operation failure:
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 Standards
Comprehensive testing is a critical part of quality assurance.
Component Testing Points
Use Testing Library to write component tests:
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)
})
State Testing Strategy
Verify the correctness of state management logic:
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 Testing Standards
Add end-to-end tests for critical business workflows:
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