Skip to content

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

🎯 最佳实践 ​

组件设计 ​

  1. 保持组件小而专注
  2. 使用 TypeScript 提供类型安全
  3. 合理使用 Hooks
  4. 避免过度优化

代码组织 ​

src/
├── components/     # 可复用组件
├── pages/         # 页面组件
├── hooks/         # 自定义 Hooks
├── store/         # 状态管理
├── utils/         # 工具函数
├── types/         # TypeScript 类型
└── styles/        # 样式文件

React 让组件化开发更简单! ⚛️