# React实战项目

# 项目结构

project/
├── src/
│   ├── components/      # 组件
│   │   ├── common/      # 通用组件
│   │   └── features/    # 功能组件
│   ├── pages/          # 页面
│   ├── hooks/          # 自定义Hooks
│   ├── store/          # 状态管理
│   │   ├── slices/     # Redux slices
│   │   └── store.js
│   ├── api/           # API接口
│   ├── utils/         # 工具函数
│   ├── styles/        # 样式文件
│   ├── App.js
│   └── index.js
├── public/
└── package.json
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

# 完整示例:待办应用

# 自定义Hook

// hooks/useTodos.js
import { useState, useEffect } from 'react'

export function useTodos() {
  const [todos, setTodos] = useState([])
  const [loading, setLoading] = useState(false)
  const [error, setError] = useState(null)

  useEffect(() => {
    fetchTodos()
  }, [])

  const fetchTodos = async () => {
    setLoading(true)
    try {
      const response = await fetch('/api/todos')
      const data = await response.json()
      setTodos(data)
    } catch (err) {
      setError(err.message)
    } finally {
      setLoading(false)
    }
  }

  const addTodo = async (text) => {
    try {
      const response = await fetch('/api/todos', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ text })
      })
      const newTodo = await response.json()
      setTodos([...todos, newTodo])
    } catch (err) {
      setError(err.message)
    }
  }

  return { todos, loading, error, addTodo, fetchTodos }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41

# 组件实现

// components/TodoList.js
import React from 'react'
import { useTodos } from '../hooks/useTodos'
import TodoItem from './TodoItem'

function TodoList() {
  const { todos, loading, error } = useTodos()

  if (loading) return <div>Loading...</div>
  if (error) return <div>Error: {error}</div>

  return (
    <ul>
      {todos.map(todo => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </ul>
  )
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

# Redux Toolkit

// store/slices/todosSlice.js
import { createSlice, createAsyncThunk } from '@reduxjs/toolkit'

export const fetchTodos = createAsyncThunk(
  'todos/fetchTodos',
  async () => {
    const response = await fetch('/api/todos')
    return response.json()
  }
)

const todosSlice = createSlice({
  name: 'todos',
  initialState: {
    items: [],
    loading: false,
    error: null
  },
  reducers: {
    addTodo: (state, action) => {
      state.items.push(action.payload)
    },
    toggleTodo: (state, action) => {
      const todo = state.items.find(t => t.id === action.payload)
      if (todo) todo.completed = !todo.completed
    }
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchTodos.pending, (state) => {
        state.loading = true
      })
      .addCase(fetchTodos.fulfilled, (state, action) => {
        state.loading = false
        state.items = action.payload
      })
      .addCase(fetchTodos.rejected, (state, action) => {
        state.loading = false
        state.error = action.error.message
      })
  }
})

export const { addTodo, toggleTodo } = todosSlice.actions
export default todosSlice.reducer
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

# 最佳实践总结

# 1. 组件设计

  • 函数组件: 优先使用函数组件和Hooks
  • 组件拆分: 保持组件小而专注
  • Props类型: 使用PropTypes或TypeScript
  • Memo优化: 合理使用React.memo

# 2. 状态管理

  • 本地状态: useState处理组件内部状态
  • 共享状态: Context或Redux
  • 服务器状态: React Query或SWR

# 3. 性能优化

  • 代码分割: React.lazy和Suspense
  • Memoization: useMemo和useCallback
  • 虚拟列表: react-window处理长列表
  • 图片优化: 使用懒加载和WebP

# 4. 测试策略

  • 单元测试: Jest测试工具函数
  • 组件测试: React Testing Library
  • E2E测试: Cypress或Playwright