React 开发最佳实践
🎯 概述
React 是一个用于构建用户界面的JavaScript库。本文将分享React开发中的最佳实践和常用技巧。
📚 核心概念
组件设计原则
- 单一职责原则
- 可复用性
- 可维护性
- 可测试性
Hooks 使用技巧
javascript
import { useState, useEffect, useCallback, useMemo } from 'react'
function UserProfile({ userId }) {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
const fetchUser = useCallback(async () => {
setLoading(true)
try {
const response = await fetch(`/api/users/${userId}`)
const userData = await response.json()
setUser(userData)
} catch (error) {
console.error('Failed to fetch user:', error)
} finally {
setLoading(false)
}
}, [userId])
useEffect(() => {
fetchUser()
}, [fetchUser])
const userDisplayName = useMemo(() => {
return user ? `${user.firstName} ${user.lastName}` : ''
}, [user])
if (loading) return <div>Loading...</div>
if (!user) return <div>User not found</div>
return (
<div>
<h1>{userDisplayName}</h1>
<p>{user.email}</p>
</div>
)
}🚀 性能优化
组件优化
javascript
// 使用 React.memo 避免不必要的重渲染
const UserCard = React.memo(({ user, onEdit }) => {
return (
<div className="user-card">
<h3>{user.name}</h3>
<button onClick={() => onEdit(user.id)}>Edit</button>
</div>
)
})
// 使用 useMemo 缓存计算结果
const ExpensiveComponent = ({ data }) => {
const processedData = useMemo(() => {
return data.map(item => ({
...item,
processed: expensiveCalculation(item)
}))
}, [data])
return <div>{/* 渲染处理后的数据 */}</div>
}代码分割
javascript
// 路由级别的代码分割
import { lazy, Suspense } from 'react'
const Home = lazy(() => import('./pages/Home'))
const About = lazy(() => import('./pages/About'))
function App() {
return (
<Router>
<Suspense fallback={<div>Loading...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</Suspense>
</Router>
)
}🛠️ 状态管理
Context API
javascript
// 创建 Context
const UserContext = createContext()
// Provider 组件
export const UserProvider = ({ children }) => {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
const value = {
user,
setUser,
loading,
login: async (credentials) => {
setLoading(true)
try {
const userData = await loginAPI(credentials)
setUser(userData)
} finally {
setLoading(false)
}
}
}
return (
<UserContext.Provider value={value}>
{children}
</UserContext.Provider>
)
}
// 自定义 Hook
export const useUser = () => {
const context = useContext(UserContext)
if (!context) {
throw new Error('useUser must be used within UserProvider')
}
return context
}Redux Toolkit
javascript
// store/slices/userSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'
export const fetchUser = createAsyncThunk(
'user/fetchUser',
async (userId) => {
const response = await fetch(`/api/users/${userId}`)
return response.json()
}
)
const userSlice = createSlice({
name: 'user',
initialState: {
data: null,
loading: false,
error: null
},
reducers: {
clearUser: (state) => {
state.data = null
}
},
extraReducers: (builder) => {
builder
.addCase(fetchUser.pending, (state) => {
state.loading = true
})
.addCase(fetchUser.fulfilled, (state, action) => {
state.loading = false
state.data = action.payload
})
.addCase(fetchUser.rejected, (state, action) => {
state.loading = false
state.error = action.error.message
})
}
})
export const { clearUser } = userSlice.actions
export default userSlice.reducer🧪 测试
单元测试
javascript
import { render, screen, fireEvent } from '@testing-library/react'
import UserCard from './UserCard'
describe('UserCard', () => {
const mockUser = {
id: 1,
name: 'John Doe',
email: 'john@example.com'
}
it('renders user information', () => {
render(<UserCard user={mockUser} />)
expect(screen.getByText('John Doe')).toBeInTheDocument()
expect(screen.getByText('john@example.com')).toBeInTheDocument()
})
it('calls onEdit when edit button is clicked', () => {
const mockOnEdit = jest.fn()
render(<UserCard user={mockUser} onEdit={mockOnEdit} />)
fireEvent.click(screen.getByText('Edit'))
expect(mockOnEdit).toHaveBeenCalledWith(1)
})
})📦 项目配置
TypeScript 配置
typescript
// types/user.ts
export interface User {
id: number
name: string
email: string
avatar?: string
}
// components/UserCard.tsx
import React from 'react'
import { User } from '../types/user'
interface UserCardProps {
user: User
onEdit: (id: number) => void
}
const UserCard: React.FC<UserCardProps> = ({ user, onEdit }) => {
return (
<div className="user-card">
<h3>{user.name}</h3>
<p>{user.email}</p>
<button onClick={() => onEdit(user.id)}>Edit</button>
</div>
)
}
export default UserCard🎯 最佳实践
组件设计
- 保持组件小而专注
- 使用 TypeScript 提供类型安全
- 合理使用 Hooks
- 避免过度优化
代码组织
src/
├── components/ # 可复用组件
├── pages/ # 页面组件
├── hooks/ # 自定义 Hooks
├── store/ # 状态管理
├── utils/ # 工具函数
├── types/ # TypeScript 类型
└── styles/ # 样式文件React 让组件化开发更简单! ⚛️