React基础到进阶

React 全面深入解析:从基础到高级实战指南

一、React 的基础知识

1. 什么是 React?

React 是由 Facebook 开发并开源的一个用于构建用户界面的 JavaScript 库。自 2013 年发布以来,React 已经发展成为前端开发领域最受欢迎的技术之一。与传统的前端开发框架(如 Angular、Vue)不同,React 的核心思想是​​组件化开发​​。

React 的核心特点:
  • ​声明式编程​​:React 采用声明式范式,让代码更加可预测且易于调试

  • ​组件化架构​​:将复杂的 UI 拆分为独立、可复用的组件

  • ​虚拟 DOM​​:通过高效的差异算法优化性能

  • ​单向数据流​​:数据自上而下流动,保证应用状态的可预测性

  • ​Learn Once, Write Anywhere​​:React 可以用于 Web、移动端(React Native)、VR 等场景

React 的发展历程:
  • 2013 年:React 首次发布,引入 JSX 和虚拟 DOM 概念

  • 2015 年:React Native 发布,扩展至移动端开发

  • 2018 年:React 16.3 引入新的生命周期方法

  • 2019 年:React 16.8 推出 Hooks,彻底改变函数组件的开发方式

  • 2020 年:React 17 作为过渡版本,为未来特性做准备

  • 2022 年:React 18 发布,引入并发特性

2. React 的基本概念

a. 组件(Components)

组件是 React 应用的构建块,每个组件都封装了自己的结构、样式和行为。

​类组件(Class Components)​​:

复制代码
import React, { Component } from 'react';

class Welcome extends Component {
  constructor(props) {
    super(props);
    this.state = {
      message: 'Hello, World!'
    };
  }
  
  componentDidMount() {
    console.log('组件已挂载');
  }
  
  render() {
    return <h1>{this.state.message}</h1>;
  }
}

export default Welcome;

​函数组件(Function Components)​​:

复制代码
import React from 'react';

function Welcome(props) {
  return <h1>Hello, {props.name}!</h1>;
}

// 或使用箭头函数
const Welcome = (props) => {
  return <h1>Hello, {props.name}!</h1>;
};

export default Welcome;
b. JSX(JavaScript XML)

JSX 是 React 的核心语法扩展,它允许我们在 JavaScript 中编写类似 HTML 的代码。

​JSX 的基本规则​​:

复制代码
// 1. 必须有一个根元素
const element = (
  <div>
    <h1>标题</h1>
    <p>段落</p>
  </div>
);

// 2. 使用 className 代替 class
const element = <div className="container">内容</div>;

// 3. 使用驼峰命名法定义属性
const element = <input defaultValue="默认值" onClick={handleClick} />;

// 4. 必须闭合所有标签
const element = <img src="image.jpg" alt="描述" />;

// 5. 在 JSX 中嵌入 JavaScript 表达式
const name = '张三';
const element = <h1>Hello, {name}!</h1>;

// 6. 条件渲染
const element = (
  <div>
    {isLoggedIn ? <UserPanel /> : <LoginForm />}
  </div>
);

// 7. 列表渲染
const numbers = [1, 2, 3, 4, 5];
const listItems = numbers.map((number) => 
  <li key={number.toString()}>{number}</li>
);
c. 虚拟 DOM(Virtual DOM)

虚拟 DOM 是 React 性能优化的核心机制,它通过以下步骤工作:

  1. ​生成虚拟 DOM​​:当组件状态变化时,React 会重新生成整个 UI 的虚拟 DOM 表示

  2. ​差异比较(Diffing)​​:React 比较新旧虚拟 DOM 的差异

  3. ​最小化更新​​:只更新实际 DOM 中发生变化的部分

    // 虚拟 DOM 的工作原理示例
    class Counter extends Component {
    state = { count: 0 };

    handleClick = () => {
    this.setState({ count: this.state.count + 1 });
    };

    render() {
    return (


    计数: {this.state.count}


    <button onClick={this.handleClick}>增加</button>

    );
    }
    }

3. React 开发环境搭建

使用 Create React App 创建项目
复制代码
# 安装 Create React App
npm install -g create-react-app

# 创建新项目
npx create-react-app my-react-app

# 进入项目目录
cd my-react-app

# 启动开发服务器
npm start
项目结构说明
复制代码
my-react-app/
├── public/
│   ├── index.html
│   └── favicon.ico
├── src/
│   ├── components/     # 组件目录
│   ├── styles/        # 样式文件
│   ├── utils/         # 工具函数
│   ├── App.js         # 根组件
│   ├── index.js       # 入口文件
│   └── index.css      # 全局样式
├── package.json
└── README.md

二、React 的进阶概念

1. 状态(State)和属性(Props)

State 的深入理解

​类组件中的 State​​:

复制代码
class Counter extends Component {
  constructor(props) {
    super(props);
    // 初始化状态
    this.state = {
      count: 0,
      isActive: true
    };
  }
  
  // 正确更新状态的方式
  increment = () => {
    this.setState(prevState => ({
      count: prevState.count + 1
    }));
  };
  
  // 批量更新
  handleMultipleUpdates = () => {
    this.setState({ count: 1 });
    this.setState({ isActive: false });
    // 等同于
    this.setState({
      count: 1,
      isActive: false
    });
  };
  
  render() {
    return (
      <div>
        <p>当前计数: {this.state.count}</p>
        <button onClick={this.increment}>增加</button>
        <p>状态: {this.state.isActive ? '活跃' : '非活跃'}</p>
      </div>
    );
  }
}

​函数组件中的 State(使用 useState Hook)​​:

复制代码
import React, { useState } from 'react';

function Counter() {
  // 使用 useState 定义状态
  const [count, setCount] = useState(0);
  const [user, setUser] = useState({ name: '', age: 0 });
  
  // 更新对象状态
  const updateUser = () => {
    setUser(prevUser => ({
      ...prevUser,
      age: prevUser.age + 1
    }));
  };
  
  // 异步更新的注意事项
  const incrementTwice = () => {
    setCount(count + 1);
    setCount(count + 1); // 这里不会立即更新,还是基于原来的 count
    
    // 正确的方式
    setCount(prevCount => prevCount + 1);
    setCount(prevCount => prevCount + 1);
  };
  
  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
      <button onClick={incrementTwice}>增加两次</button>
    </div>
  );
}
Props 的深入理解

​Props 的基本使用​​:

复制代码
// 父组件
function App() {
  const user = { name: '张三', age: 25 };
  const numbers = [1, 2, 3, 4, 5];
  
  return (
    <div>
      <UserCard 
        name={user.name} 
        age={user.age}
        isVerified={true}
        onLogin={() => console.log('用户登录')}
      />
      <NumberList numbers={numbers} />
    </div>
  );
}

// 子组件 - 函数组件
function UserCard(props) {
  return (
    <div className="user-card">
      <h2>{props.name}</h2>
      <p>年龄: {props.age}</p>
      {props.isVerified && <span>已验证</span>}
      <button onClick={props.onLogin}>登录</button>
    </div>
  );
}

// 子组件 - 使用解构赋值
function NumberList({ numbers, title = "数字列表" }) {
  return (
    <div>
      <h3>{title}</h3>
      <ul>
        {numbers.map(num => (
          <li key={num}>{num}</li>
        ))}
      </ul>
    </div>
  );
}

// Props 的默认值
NumberList.defaultProps = {
  title: "默认标题"
};

// Props 的类型检查
import PropTypes from 'prop-types';

NumberList.propTypes = {
  numbers: PropTypes.array.isRequired,
  title: PropTypes.string
};

​Props 的高级用法​​:

复制代码
// Children 属性
function Container({ children, className }) {
  return <div className={`container ${className}`}>{children}</div>;
}

function App() {
  return (
    <Container className="main">
      <h1>标题</h1>
      <p>内容</p>
    </Container>
  );
}

// 渲染属性模式(Render Props)
class DataProvider extends Component {
  state = { data: null, loading: true };
  
  componentDidMount() {
    fetch('/api/data')
      .then(response => response.json())
      .then(data => this.setState({ data, loading: false }));
  }
  
  render() {
    return this.props.children(this.state);
  }
}

// 使用
<DataProvider>
  {({ data, loading }) => (
    loading ? <div>加载中...</div> : <div>{data}</div>
  )}
</DataProvider>

2. 生命周期方法(Lifecycle Methods)

类组件的生命周期
复制代码
class LifecycleDemo extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
    console.log('1. constructor - 构造函数');
  }
  
  static getDerivedStateFromProps(props, state) {
    console.log('2. getDerivedStateFromProps - 从 props 派生状态');
    return null;
  }
  
  componentDidMount() {
    console.log('4. componentDidMount - 组件挂载完成');
    // 适合进行数据获取、订阅事件等操作
  }
  
  shouldComponentUpdate(nextProps, nextState) {
    console.log('5. shouldComponentUpdate - 是否应该更新');
    return true; // 返回 false 可以阻止重新渲染
  }
  
  getSnapshotBeforeUpdate(prevProps, prevState) {
    console.log('6. getSnapshotBeforeUpdate - 获取更新前的快照');
    return null;
  }
  
  componentDidUpdate(prevProps, prevState, snapshot) {
    console.log('7. componentDidUpdate - 组件更新完成');
    // 适合在更新后执行操作
  }
  
  componentWillUnmount() {
    console.log('8. componentWillUnmount - 组件即将卸载');
    // 清理工作:取消订阅、清除定时器等
  }
  
  handleClick = () => {
    this.setState({ count: this.state.count + 1 });
  };
  
  render() {
    console.log('3. render - 渲染');
    return (
      <div>
        <p>计数: {this.state.count}</p>
        <button onClick={this.handleClick}>增加</button>
      </div>
    );
  }
}
生命周期图示
复制代码
挂载阶段:
constructor → getDerivedStateFromProps → render → componentDidMount

更新阶段:
getDerivedStateFromProps → shouldComponentUpdate → render → getSnapshotBeforeUpdate → componentDidUpdate

卸载阶段:
componentWillUnmount

3. 钩子(Hooks)

Hooks 是 React 16.8 引入的革命性特性,让函数组件能够使用状态和其他 React 特性。

基础 Hooks

​useState​​:

复制代码
import React, { useState } from 'react';

function Example() {
  // 基本类型状态
  const [count, setCount] = useState(0);
  
  // 对象类型状态
  const [user, setUser] = useState({
    name: '',
    email: '',
    age: 0
  });
  
  // 数组类型状态
  const [items, setItems] = useState([]);
  
  // 更新对象状态
  const updateUser = (field, value) => {
    setUser(prevUser => ({
      ...prevUser,
      [field]: value
    }));
  };
  
  // 更新数组状态
  const addItem = (item) => {
    setItems(prevItems => [...prevItems, item]);
  };
  
  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={() => setCount(count + 1)}>增加</button>
      
      <input 
        value={user.name}
        onChange={(e) => updateUser('name', e.target.value)}
        placeholder="姓名"
      />
    </div>
  );
}

​useEffect​​:

复制代码
import React, { useState, useEffect } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [userId, setUserId] = useState(1);
  
  // 1. 无依赖数组 - 每次渲染后都执行
  useEffect(() => {
    console.log('组件渲染完成');
  });
  
  // 2. 空依赖数组 - 仅在挂载时执行一次
  useEffect(() => {
    console.log('组件挂载完成');
    
    // 清理函数
    return () => {
      console.log('组件即将卸载');
    };
  }, []);
  
  // 3. 有依赖数组 - 依赖变化时执行
  useEffect(() => {
    const fetchData = async () => {
      setLoading(true);
      try {
        const response = await fetch(`/api/users/${userId}`);
        const userData = await response.json();
        setData(userData);
      } catch (error) {
        console.error('获取数据失败:', error);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
    
    // 清理函数:取消请求或清理副作用
    return () => {
      // 取消请求的逻辑
    };
  }, [userId]); // 依赖数组
  
  if (loading) return <div>加载中...</div>;
  
  return (
    <div>
      <h1>用户信息</h1>
      <p>姓名: {data.name}</p>
      <button onClick={() => setUserId(userId + 1)}>下一个用户</button>
    </div>
  );
}

​useContext​​:

复制代码
import React, { createContext, useContext, useState } from 'react';

// 创建 Context
const ThemeContext = createContext();
const UserContext = createContext();

// 提供者组件
function App() {
  const [theme, setTheme] = useState('light');
  const [user, setUser] = useState({ name: '张三', role: 'admin' });
  
  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <UserContext.Provider value={{ user, setUser }}>
        <Header />
        <MainContent />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}

// 消费者组件
function Header() {
  const { theme, setTheme } = useContext(ThemeContext);
  const { user } = useContext(UserContext);
  
  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light');
  };
  
  return (
    <header className={`header-${theme}`}>
      <h1>欢迎, {user.name}</h1>
      <button onClick={toggleTheme}>
        切换主题: {theme === 'light' ? '暗色' : '亮色'}
      </button>
    </header>
  );
}

function MainContent() {
  const { theme } = useContext(ThemeContext);
  
  return (
    <main className={`main-${theme}`}>
      <p>这是主要内容区域</p>
    </main>
  );
}
高级 Hooks

​useReducer​​:

复制代码
import React, { useReducer } from 'react';

// 初始状态
const initialState = {
  count: 0,
  history: []
};

// Reducer 函数
function counterReducer(state, action) {
  switch (action.type) {
    case 'INCREMENT':
      return {
        ...state,
        count: state.count + 1,
        history: [...state.history, { type: 'INCREMENT', value: state.count + 1 }]
      };
    case 'DECREMENT':
      return {
        ...state,
        count: state.count - 1,
        history: [...state.history, { type: 'DECREMENT', value: state.count - 1 }]
      };
    case 'RESET':
      return initialState;
    case 'SET_COUNT':
      return {
        ...state,
        count: action.payload,
        history: [...state.history, { type: 'SET', value: action.payload }]
      };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(counterReducer, initialState);
  
  return (
    <div>
      <p>当前计数: {state.count}</p>
      <button onClick={() => dispatch({ type: 'INCREMENT' })}>增加</button>
      <button onClick={() => dispatch({ type: 'DECREMENT' })}>减少</button>
      <button onClick={() => dispatch({ type: 'RESET' })}>重置</button>
      <button onClick={() => dispatch({ type: 'SET_COUNT', payload: 10 })}>
        设置为10
      </button>
      
      <h3>操作历史:</h3>
      <ul>
        {state.history.map((entry, index) => (
          <li key={index}>{entry.type}: {entry.value}</li>
        ))}
      </ul>
    </div>
  );
}

​useMemo 和 useCallback​​:

复制代码
import React, { useState, useMemo, useCallback } from 'react';

function ExpensiveCalculation({ number }) {
  // 使用 useMemo 缓存计算结果
  const result = useMemo(() => {
    console.log('执行昂贵计算...');
    let sum = 0;
    for (let i = 0; i < number; i++) {
      sum += i;
    }
    return sum;
  }, [number]); // 只有当 number 变化时才重新计算
  
  return <div>计算结果: {result}</div>;
}

function UserList({ users, onUserClick }) {
  // 使用 useMemo 缓存过滤结果
  const activeUsers = useMemo(() => {
    return users.filter(user => user.isActive);
  }, [users]);
  
  return (
    <ul>
      {activeUsers.map(user => (
        <li key={user.id} onClick={() => onUserClick(user)}>
          {user.name}
        </li>
      ))}
    </ul>
  );
}

function App() {
  const [count, setCount] = useState(0);
  const [users, setUsers] = useState([
    { id: 1, name: '张三', isActive: true },
    { id: 2, name: '李四', isActive: false },
    { id: 3, name: '王五', isActive: true }
  ]);
  
  // 使用 useCallback 缓存函数
  const handleUserClick = useCallback((user) => {
    console.log('用户被点击:', user.name);
  }, []); // 空依赖数组表示函数不会改变
  
  const addUser = useCallback((name) => {
    setUsers(prevUsers => [
      ...prevUsers,
      { id: Date.now(), name, isActive: true }
    ]);
  }, []);
  
  return (
    <div>
      <button onClick={() => setCount(count + 1)}>计数: {count}</button>
      <ExpensiveCalculation number={1000000} />
      <UserList users={users} onUserClick={handleUserClick} />
      <button onClick={() => addUser('新用户')}>添加用户</button>
    </div>
  );
}

​自定义 Hooks​​:

复制代码
import { useState, useEffect } from 'react';

// 自定义 Hook:数据获取
function useFetch(url) {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        const response = await fetch(url);
        if (!response.ok) {
          throw new Error('网络响应不正常');
        }
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, [url]);
  
  return { data, loading, error };
}

// 自定义 Hook:本地存储
function useLocalStorage(key, initialValue) {
  const [storedValue, setStoredValue] = useState(() => {
    try {
      const item = window.localStorage.getItem(key);
      return item ? JSON.parse(item) : initialValue;
    } catch (error) {
      console.error(`读取 localStorage 键 "${key}" 时出错:`, error);
      return initialValue;
    }
  });
  
  const setValue = (value) => {
    try {
      setStoredValue(value);
      window.localStorage.setItem(key, JSON.stringify(value));
    } catch (error) {
      console.error(`设置 localStorage 键 "${key}" 时出错:`, error);
    }
  };
  
  return [storedValue, setValue];
}

// 使用自定义 Hooks
function UserProfile({ userId }) {
  const { data: user, loading, error } = useFetch(`/api/users/${userId}`);
  const [theme, setTheme] = useLocalStorage('theme', 'light');
  
  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误: {error}</div>;
  
  return (
    <div className={`profile-${theme}`}>
      <h1>{user.name}</h1>
      <p>邮箱: {user.email}</p>
      <button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
        切换主题
      </button>
    </div>
  );
}

4. 事件处理(Event Handling)

复制代码
function EventHandling() {
  const [form, setForm] = useState({
    username: '',
    password: '',
    rememberMe: false
  });
  
  // 基本事件处理
  const handleClick = (event) => {
    event.preventDefault();
    console.log('按钮被点击了', event);
  };
  
  // 输入处理
  const handleInputChange = (event) => {
    const { name, value, type, checked } = event.target;
    setForm(prevForm => ({
      ...prevForm,
      [name]: type === 'checkbox' ? checked : value
    }));
  };
  
  // 表单提交
  const handleSubmit = (event) => {
    event.preventDefault();
    console.log('表单提交:', form);
  };
  
  // 键盘事件
  const handleKeyPress = (event) => {
    if (event.key === 'Enter') {
      console.log('Enter 键被按下');
    }
  };
  
  // 合成事件池(React 17 之前)
  const handleEventPool = (event) => {
    // React 17 之前需要持久化事件对象
    event.persist();
    setTimeout(() => {
      console.log(event.type);
    }, 1000);
  };
  
  return (
    <form onSubmit={handleSubmit}>
      <input
        name="username"
        value={form.username}
        onChange={handleInputChange}
        onKeyPress={handleKeyPress}
        placeholder="用户名"
      />
      
      <input
        name="password"
        type="password"
        value={form.password}
        onChange={handleInputChange}
        placeholder="密码"
      />
      
      <label>
        <input
          name="rememberMe"
          type="checkbox"
          checked={form.rememberMe}
          onChange={handleInputChange}
        />
        记住我
      </label>
      
      <button type="submit" onClick={handleClick}>
        登录
      </button>
    </form>
  );
}

三、React 的高级应用

1. 状态管理(State Management)

a. Redux 状态管理

​Redux 基础设置​​:

复制代码
// store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';

// Action Types
const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';
const SET_USER = 'SET_USER';

// Action Creators
export const increment = () => ({ type: INCREMENT });
export const decrement = () => ({ type: DECREMENT });
export const setUser = (user) => ({ type: SET_USER, payload: user });

// 异步 Action
export const fetchUser = (userId) => {
  return async (dispatch) => {
    try {
      const response = await fetch(`/api/users/${userId}`);
      const user = await response.json();
      dispatch(setUser(user));
    } catch (error) {
      console.error('获取用户失败:', error);
    }
  };
};

// Reducers
const counterReducer = (state = 0, action) => {
  switch (action.type) {
    case INCREMENT:
      return state + 1;
    case DECREMENT:
      return state - 1;
    default:
      return state;
  }
};

const userReducer = (state = null, action) => {
  switch (action.type) {
    case SET_USER:
      return action.payload;
    default:
      return state;
  }
};

const rootReducer = combineReducers({
  counter: counterReducer,
  user: userReducer
});

// 创建 Store
const store = createStore(
  rootReducer,
  composeWithDevTools(applyMiddleware(thunk))
);

export default store;

​React-Redux 连接​​:

复制代码
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import store from './store';
import App from './App';

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById('root')
);

// Counter.js
import React from 'react';
import { connect } from 'react-redux';
import { increment, decrement } from './store';

function Counter({ count, increment, decrement }) {
  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={increment}>增加</button>
      <button onClick={decrement}>减少</button>
    </div>
  );
}

const mapStateToProps = (state) => ({
  count: state.counter
});

const mapDispatchToProps = {
  increment,
  decrement
};

export default connect(mapStateToProps, mapDispatchToProps)(Counter);

​Redux Toolkit(现代 Redux)​​:

复制代码
// store.js
import { configureStore, createSlice } from '@reduxjs/toolkit';

const counterSlice = createSlice({
  name: 'counter',
  initialState: 0,
  reducers: {
    increment: (state) => state + 1,
    decrement: (state) => state - 1,
    incrementByAmount: (state, action) => state + action.payload
  }
});

const userSlice = createSlice({
  name: 'user',
  initialState: null,
  reducers: {
    setUser: (state, action) => action.payload,
    clearUser: () => null
  }
});

export const { increment, decrement, incrementByAmount } = counterSlice.actions;
export const { setUser, clearUser } = userSlice.actions;

const store = configureStore({
  reducer: {
    counter: counterSlice.reducer,
    user: userSlice.reducer
  }
});

export default store;

// 在组件中使用
import React from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { increment, decrement } from './store';

function Counter() {
  const count = useSelector(state => state.counter);
  const dispatch = useDispatch();
  
  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={() => dispatch(increment())}>增加</button>
      <button onClick={() => dispatch(decrement())}>减少</button>
    </div>
  );
}
b. Context API 状态管理

​创建 Context​​:

复制代码
// contexts/AppContext.js
import React, { createContext, useContext, useReducer } from 'react';

const AppContext = createContext();

// 初始状态
const initialState = {
  user: null,
  theme: 'light',
  notifications: [],
  loading: false
};

// Reducer 函数
function appReducer(state, action) {
  switch (action.type) {
    case 'SET_USER':
      return { ...state, user: action.payload };
    case 'SET_THEME':
      return { ...state, theme: action.payload };
    case 'ADD_NOTIFICATION':
      return { 
        ...state, 
        notifications: [...state.notifications, action.payload] 
      };
    case 'SET_LOADING':
      return { ...state, loading: action.payload };
    default:
      return state;
  }
}

// Provider 组件
export function AppProvider({ children }) {
  const [state, dispatch] = useReducer(appReducer, initialState);
  
  // Action creators
  const setUser = (user) => dispatch({ type: 'SET_USER', payload: user });
  const setTheme = (theme) => dispatch({ type: 'SET_THEME', payload: theme });
  const addNotification = (message) => 
    dispatch({ type: 'ADD_NOTIFICATION', payload: { id: Date.now(), message } });
  const setLoading = (loading) => dispatch({ type: 'SET_LOADING', payload: loading });
  
  const value = {
    ...state,
    setUser,
    setTheme,
    addNotification,
    setLoading
  };
  
  return (
    <AppContext.Provider value={value}>
      {children}
    </AppContext.Provider>
  );
}

// 自定义 Hook
export function useApp() {
  const context = useContext(AppContext);
  if (!context) {
    throw new Error('useApp 必须在 AppProvider 内部使用');
  }
  return context;
}

​使用 Context​​:

复制代码
// App.js
import React from 'react';
import { AppProvider } from './contexts/AppContext';
import Header from './components/Header';
import MainContent from './components/MainContent';
import NotificationCenter from './components/NotificationCenter';

function App() {
  return (
    <AppProvider>
      <div className="app">
        <Header />
        <MainContent />
        <NotificationCenter />
      </div>
    </AppProvider>
  );
}

// Header.js
import React from 'react';
import { useApp } from '../contexts/AppContext';

function Header() {
  const { user, theme, setTheme } = useApp();
  
  const toggleTheme = () => {
    setTheme(theme === 'light' ? 'dark' : 'light');
  };
  
  return (
    <header className={`header-${theme}`}>
      <h1>我的应用</h1>
      {user && <span>欢迎, {user.name}</span>}
      <button onClick={toggleTheme}>
        {theme === 'light' ? '切换到暗色主题' : '切换到亮色主题'}
      </button>
    </header>
  );
}

2. 路由(Routing)

​React Router 基础​​:

复制代码
// App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route, Link } from 'react-router-dom';
import Home from './pages/Home';
import About from './pages/About';
import User from './pages/User';
import NotFound from './pages/NotFound';

function App() {
  return (
    <Router>
      <nav>
        <Link to="/">首页</Link>
        <Link to="/about">关于</Link>
        <Link to="/user/123">用户123</Link>
      </nav>
      
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/user/:id" element={<User />} />
        <Route path="*" element={<NotFound />} />
      </Routes>
    </Router>
  );
}

// User.js
import React from 'react';
import { useParams, useNavigate, useLocation } from 'react-router-dom';

function User() {
  const { id } = useParams();
  const navigate = useNavigate();
  const location = useLocation();
  
  const goBack = () => {
    navigate(-1); // 返回上一页
  };
  
  const goToAbout = () => {
    navigate('/about', { replace: true }); // 替换当前历史记录
  };
  
  return (
    <div>
      <h1>用户页面</h1>
      <p>用户ID: {id}</p>
      <p>当前路径: {location.pathname}</p>
      <button onClick={goBack}>返回</button>
      <button onClick={goToAbout}>关于页面</button>
    </div>
  );
}

​路由保护​​:

复制代码
// ProtectedRoute.js
import React from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import { useAuth } from '../contexts/AuthContext';

function ProtectedRoute({ children }) {
  const { user, loading } = useAuth();
  const location = useLocation();
  
  if (loading) {
    return <div>加载中...</div>;
  }
  
  if (!user) {
    // 重定向到登录页面,并保存当前路径
    return <Navigate to="/login" state={{ from: location }} replace />;
  }
  
  return children;
}

// 使用
<Routes>
  <Route path="/dashboard" element={
    <ProtectedRoute>
      <Dashboard />
    </ProtectedRoute>
  } />
</Routes>

3. 异步数据处理

​数据获取模式​​:

复制代码
import React, { useState, useEffect } from 'react';

function DataFetching() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);
  
  useEffect(() => {
    const fetchData = async () => {
      try {
        setLoading(true);
        setError(null);
        
        const response = await fetch('/api/data');
        if (!response.ok) {
          throw new Error(`HTTP error! status: ${response.status}`);
        }
        
        const result = await response.json();
        setData(result);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };
    
    fetchData();
  }, []); // 空依赖数组表示只在挂载时执行
  
  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误: {error}</div>;
  if (!data) return <div>暂无数据</div>;
  
  return (
    <div>
      <h1>数据列表</h1>
      <ul>
        {data.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

​优化数据获取​​:

复制代码
import React, { useState, useEffect, useRef } from 'react';

function OptimizedDataFetching() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState(null);
  const abortControllerRef = useRef(null);
  
  useEffect(() => {
    return () => {
      // 组件卸载时中止请求
      if (abortControllerRef.current) {
        abortControllerRef.current.abort();
      }
    };
  }, []);
  
  const fetchData = async (url) => {
    if (abortControllerRef.current) {
      abortControllerRef.current.abort(); // 中止之前的请求
    }
    
    abortControllerRef.current = new AbortController();
    
    try {
      setLoading(true);
      setError(null);
      
      const response = await fetch(url, {
        signal: abortControllerRef.current.signal
      });
      
      if (!response.ok) throw new Error('请求失败');
      
      const result = await response.json();
      setData(result);
    } catch (err) {
      if (err.name !== 'AbortError') {
        setError(err.message);
      }
    } finally {
      setLoading(false);
      abortControllerRef.current = null;
    }
  };
  
  return (
    <div>
      <button onClick={() => fetchData('/api/data')}>获取数据</button>
      {loading && <div>加载中...</div>}
      {/* 渲染数据 */}
    </div>
  );
}

4. 性能优化(Performance Optimization)

​React.memo​​:

复制代码
import React, { memo } from 'react';

// 普通组件 - 每次父组件渲染都会重新渲染
function UserItem({ user, onEdit }) {
  console.log('UserItem 渲染:', user.id);
  return (
    <li>
      {user.name}
      <button onClick={() => onEdit(user)}>编辑</button>
    </li>
  );
}

// 使用 memo 包装的组件 - 只有 props 变化时才会重新渲染
const OptimizedUserItem = memo(UserItem, (prevProps, nextProps) => {
  // 自定义比较函数
  return prevProps.user.id === nextProps.user.id && 
         prevProps.onEdit === nextProps.onEdit;
});

// 使用 useCallback 优化函数引用
function UserList() {
  const [users, setUsers] = useState([]);
  const [selectedUser, setSelectedUser] = useState(null);
  
  // 使用 useCallback 缓存函数,避免不必要的重新渲染
  const handleEdit = useCallback((user) => {
    setSelectedUser(user);
  }, []);
  
  return (
    <div>
      {users.map(user => (
        <OptimizedUserItem 
          key={user.id} 
          user={user} 
          onEdit={handleEdit} 
        />
      ))}
    </div>
  );
}

​useMemo 优化复杂计算​​:

复制代码
import React, { useMemo } from 'react';

function ExpensiveComponent({ data, filter }) {
  // 使用 useMemo 缓存计算结果
  const filteredData = useMemo(() => {
    console.log('执行过滤计算...');
    return data.filter(item => 
      item.name.toLowerCase().includes(filter.toLowerCase())
    );
  }, [data, filter]); // 只有当 data 或 filter 变化时才重新计算
  
  // 另一个使用 useMemo 的例子:配置对象
  const config = useMemo(() => ({
    maxItems: 10,
    theme: 'dark',
    onItemClick: () => console.log('项目被点击')
  }), []); // 空依赖数组表示这个对象永远不会改变
  
  return (
    <div>
      <h2>过滤结果 ({filteredData.length} 项)</h2>
      <ul>
        {filteredData.map(item => (
          <li key={item.id}>{item.name}</li>
        ))}
      </ul>
    </div>
  );
}

​代码分割(懒加载)​​:

复制代码
import React, { lazy, Suspense } from 'react';

// 使用 lazy 进行代码分割
const LazyComponent = lazy(() => import('./LazyComponent'));
const AnotherLazyComponent = lazy(() => import('./AnotherLazyComponent'));

function App() {
  const [showLazy, setShowLazy] = useState(false);
  
  return (
    <div>
      <button onClick={() => setShowLazy(true)}>加载懒组件</button>
      
      {showLazy && (
        <Suspense fallback={<div>加载中...</div>}>
          <LazyComponent />
          <AnotherLazyComponent />
        </Suspense>
      )}
    </div>
  );
}

// 路由级别的代码分割
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));

function App() {
  return (
    <Router>
      <Suspense fallback={<div>页面加载中...</div>}>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/about" element={<About />} />
        </Routes>
      </Suspense>
    </Router>
  );
}

四、实际应用案例

1. 完整的 Todo 应用

复制代码
import React, { useState, useReducer, useMemo, useCallback } from 'react';

// Reducer 函数
function todoReducer(state, action) {
  switch (action.type) {
    case 'ADD_TODO':
      return {
        ...state,
        todos: [...state.todos, {
          id: Date.now(),
          text: action.payload,
          completed: false,
          createdAt: new Date().toISOString()
        }]
      };
      
    case 'TOGGLE_TODO':
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload
            ? { ...todo, completed: !todo.completed }
            : todo
        )
      };
      
    case 'DELETE_TODO':
      return {
        ...state,
        todos: state.todos.filter(todo => todo.id !== action.payload)
      };
      
    case 'EDIT_TODO':
      return {
        ...state,
        todos: state.todos.map(todo =>
          todo.id === action.payload.id
            ? { ...todo, text: action.payload.text }
            : todo
        )
      };
      
    case 'SET_FILTER':
      return {
        ...state,
        filter: action.payload
      };
      
    default:
      return state;
  }
}

// 初始状态
const initialState = {
  todos: [],
  filter: 'all' // all, active, completed
};

function TodoApp() {
  const [state, dispatch] = useReducer(todoReducer, initialState);
  const [inputValue, setInputValue] = useState('');
  const [editingId, setEditingId] = useState(null);
  const [editText, setEditText] = useState('');
  
  // 添加待办事项
  const addTodo = useCallback(() => {
    if (inputValue.trim()) {
      dispatch({ type: 'ADD_TODO', payload: inputValue.trim() });
      setInputValue('');
    }
  }, [inputValue]);
  
  // 切换完成状态
  const toggleTodo = useCallback((id) => {
    dispatch({ type: 'TOGGLE_TODO', payload: id });
  }, []);
  
  // 删除待办事项
  const deleteTodo = useCallback((id) => {
    dispatch({ type: 'DELETE_TODO', payload: id });
  }, []);
  
  // 开始编辑
  const startEdit = useCallback((todo) => {
    setEditingId(todo.id);
    setEditText(todo.text);
  }, []);
  
  // 保存编辑
  const saveEdit = useCallback(() => {
    if (editText.trim()) {
      dispatch({
        type: 'EDIT_TODO',
        payload: { id: editingId, text: editText.trim() }
      });
      setEditingId(null);
      setEditText('');
    }
  }, [editText, editingId]);
  
  // 取消编辑
  const cancelEdit = useCallback(() => {
    setEditingId(null);
    setEditText('');
  }, []);
  
  // 过滤待办事项
  const filteredTodos = useMemo(() => {
    switch (state.filter) {
      case 'active':
        return state.todos.filter(todo => !todo.completed);
      case 'completed':
        return state.todos.filter(todo => todo.completed);
      default:
        return state.todos;
    }
  }, [state.todos, state.filter]);
  
  // 统计信息
  const stats = useMemo(() => {
    const total = state.todos.length;
    const completed = state.todos.filter(todo => todo.completed).length;
    const active = total - completed;
    
    return { total, completed, active };
  }, [state.todos]);
  
  return (
    <div className="todo-app">
      <h1>Todo 应用</h1>
      
      {/* 添加新待办事项 */}
      <div className="add-todo">
        <input
          type="text"
          value={inputValue}
          onChange={(e) => setInputValue(e.target.value)}
          onKeyPress={(e) => e.key === 'Enter' && addTodo()}
          placeholder="添加新的待办事项..."
        />
        <button onClick={addTodo}>添加</button>
      </div>
      
      {/* 过滤选项 */}
      <div className="filters">
        <button 
          className={state.filter === 'all' ? 'active' : ''}
          onClick={() => dispatch({ type: 'SET_FILTER', payload: 'all' })}
        >
          全部 ({stats.total})
        </button>
        <button 
          className={state.filter === 'active' ? 'active' : ''}
          onClick={() => dispatch({ type: 'SET_FILTER', payload: 'active' })}
        >
          待完成 ({stats.active})
        </button>
        <button 
          className={state.filter === 'completed' ? 'active' : ''}
          onClick={() => dispatch({ type: 'SET_FILTER', payload: 'completed' })}
        >
          已完成 ({stats.completed})
        </button>
      </div>
      
      {/* 待办事项列表 */}
      <ul className="todo-list">
        {filteredTodos.map(todo => (
          <li key={todo.id} className={`todo-item ${todo.completed ? 'completed' : ''}`}>
            {editingId === todo.id ? (
              // 编辑模式
              <div className="edit-mode">
                <input
                  type="text"
                  value={editText}
                  onChange={(e) => setEditText(e.target.value)}
                  onKeyPress={(e) => {
                    if (e.key === 'Enter') saveEdit();
                    if (e.key === 'Escape') cancelEdit();
                  }}
                  autoFocus
                />
                <button onClick={saveEdit}>保存</button>
                <button onClick={cancelEdit}>取消</button>
              </div>
            ) : (
              // 查看模式
              <div className="view-mode">
                <input
                  type="checkbox"
                  checked={todo.completed}
                  onChange={() => toggleTodo(todo.id)}
                />
                <span 
                  className="todo-text"
                  onDoubleClick={() => startEdit(todo)}
                >
                  {todo.text}
                </span>
                <div className="actions">
                  <button onClick={() => startEdit(todo)}>编辑</button>
                  <button onClick={() => deleteTodo(todo.id)}>删除</button>
                </div>
              </div>
            )}
          </li>
        ))}
      </ul>
      
      {/* 空状态 */}
      {filteredTodos.length === 0 && (
        <div className="empty-state">
          {state.filter === 'all' && '暂无待办事项'}
          {state.filter === 'active' && '暂无待完成的待办事项'}
          {state.filter === 'completed' && '暂无已完成的待办事项'}
        </div>
      )}
    </div>
  );
}

export default TodoApp;

2. 数据可视化仪表板

复制代码
import React, { useState, useEffect, useMemo } from 'react';
import { useFetch } from '../hooks/useFetch';

function Dashboard() {
  const [timeRange, setTimeRange] = useState('week');
  const { data: stats, loading, error } = useFetch(`/api/stats?range=${timeRange}`);
  
  // 处理图表数据
  const chartData = useMemo(() => {
    if (!stats) return null;
    
    return {
      labels: stats.chartData.labels,
      datasets: [
        {
          label: '用户活跃度',
          data: stats.chartData.values,
          backgroundColor: 'rgba(54, 162, 235, 0.2)',
          borderColor: 'rgba(54, 162, 235, 1)',
          borderWidth: 2
        }
      ]
    };
  }, [stats]);
  
  if (loading) return <div className="loading">加载中...</div>;
  if (error) return <div className="error">错误: {error}</div>;
  if (!stats) return <div>暂无数据</div>;
  
  return (
    <div className="dashboard">
      <div className="dashboard-header">
        <h1>数据仪表板</h1>
        <div className="time-range-selector">
          <button 
            className={timeRange === 'day' ? 'active' : ''}
            onClick={() => setTimeRange('day')}
          >
            今日
          </button>
          <button 
            className={timeRange === 'week' ? 'active' : ''}
            onClick={() => setTimeRange('week')}
          >
            本周
          </button>
          <button 
            className={timeRange === 'month' ? 'active' : ''}
            onClick={() => setTimeRange('month')}
          >
            本月
          </button>
        </div>
      </div>
      
      <div className="stats-grid">
        <StatCard 
          title="总用户数" 
          value={stats.totalUsers} 
          change={stats.userGrowth} 
          icon="👥"
        />
        <StatCard 
          title="活跃用户" 
          value={stats.activeUsers} 
          change={stats.activeGrowth} 
          icon="🔥"
        />
        <StatCard 
          title="总收入" 
          value={`$${stats.revenue}`} 
          change={stats.revenueGrowth} 
          icon="💰"
        />
        <StatCard 
          title="转化率" 
          value={`${stats.conversionRate}%`} 
          change={stats.conversionGrowth} 
          icon="📈"
        />
      </div>
      
      <div className="charts">
        <div className="chart-container">
          <h3>用户活跃趋势</h3>
          {/* 这里可以集成 Chart.js 或其他图表库 */}
          <div className="chart-placeholder">
            {/* 实际项目中这里会渲染真实的图表 */}
            <p>图表组件占位符</p>
            <pre>{JSON.stringify(chartData, null, 2)}</pre>
          </div>
        </div>
      </div>
    </div>
  );
}

function StatCard({ title, value, change, icon }) {
  const isPositive = change >= 0;
  
  return (
    <div className="stat-card">
      <div className="stat-header">
        <span className="stat-icon">{icon}</span>
        <span className="stat-title">{title}</span>
      </div>
      <div className="stat-value">{value}</div>
      <div className={`stat-change ${isPositive ? 'positive' : 'negative'}`}>
        {isPositive ? '↑' : '↓'} {Math.abs(change)}%
      </div>
    </div>
  );
}

export default Dashboard;

五、React 最佳实践和常见模式

1. 组件设计原则

​单一职责原则​​:

复制代码
// 不好的做法:一个组件做太多事情
function UserProfile() {
  // 这个组件同时处理用户信息、设置、历史记录...
  return <div>...</div>;
}

// 好的做法:拆分为专注的组件
function UserProfile() {
  return (
    <div>
      <UserInfo />
      <UserSettings />
      <UserHistory />
    </div>
  );
}

function UserInfo({ user }) {
  return (
    <div className="user-info">
      <img src={user.avatar} alt={user.name} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
    </div>
  );
}

​组合优于继承​​:

复制代码
// 使用组合模式
function Card({ children, className }) {
  return <div className={`card ${className}`}>{children}</div>;
}

function UserCard({ user }) {
  return (
    <Card className="user-card">
      <h3>{user.name}</h3>
      <p>{user.email}</p>
    </Card>
  );
}

function ProductCard({ product }) {
  return (
    <Card className="product-card">
      <h3>{product.name}</h3>
      <p>${product.price}</p>
    </Card>
  );
}

2. 错误边界(Error Boundaries)

复制代码
import React from 'react';

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false, error: null, errorInfo: null };
  }
  
  static getDerivedStateFromError(error) {
    return { hasError: true };
  }
  
  componentDidCatch(error, errorInfo) {
    this.setState({
      error: error,
      errorInfo: errorInfo
    });
    
    // 可以在这里记录错误到错误报告服务
    console.error('错误边界捕获到错误:', error, errorInfo);
  }
  
  render() {
    if (this.state.hasError) {
      return (
        <div className="error-boundary">
          <h2>出了点问题。</h2>
          <details>
            {this.state.error && this.state.error.toString()}
            <br />
            {this.state.errorInfo.componentStack}
          </details>
          <button onClick={() => this.setState({ hasError: false })}>
            重试
          </button>
        </div>
      );
    }
    
    return this.props.children;
  }
}

// 使用
function App() {
  return (
    <ErrorBoundary>
      <MyComponent />
    </ErrorBoundary>
  );
}

3. 测试策略

复制代码
// MyComponent.js
import React from 'react';

function MyComponent({ onClick, value }) {
  return (
    <div>
      <button onClick={onClick}>点击我</button>
      <span data-testid="value">{value}</span>
    </div>
  );
}

export default MyComponent;

// MyComponent.test.js
import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import MyComponent from './MyComponent';

test('渲染组件并显示正确的值', () => {
  render(<MyComponent value="测试值" />);
  expect(screen.getByTestId('value')).toHaveTextContent('测试值');
});

test('点击按钮时调用 onClick', () => {
  const handleClick = jest.fn();
  render(<MyComponent onClick={handleClick} />);
  
  fireEvent.click(screen.getByText('点击我'));
  expect(handleClick).toHaveBeenCalledTimes(1);
});

六、React 18 新特性

1. 并发特性(Concurrent Features)

复制代码
import React, { useState, Suspense, useTransition } from 'react';

function SearchResults({ query }) {
  if (query === '') {
    return null;
  }
  
  // 模拟数据加载
  const results = use(fetchResults(query));
  
  return (
    <ul>
      {results.map(result => (
        <li key={result.id}>{result.name}</li>
      ))}
    </ul>
  );
}

function SearchPage() {
  const [query, setQuery] = useState('');
  const [isPending, startTransition] = useTransition();
  
  const handleChange = (e) => {
    const value = e.target.value;
    
    // 紧急更新:立即更新输入框
    setQuery(value);
    
    // 非紧急更新:标记搜索结果更新可以中断
    startTransition(() => {
      setQuery(value);
    });
  };
  
  return (
    <div>
      <input 
        type="text" 
        value={query} 
        onChange={handleChange} 
      />
      
      {/* 显示加载状态 */}
      {isPending && <div>加载中...</div>}
      
      <Suspense fallback={<div>搜索中...</div>}>
        <SearchResults query={query} />
      </Suspense>
    </div>
  );
}

// 注意:use 钩子目前还是实验性功能

2. 自动批处理(Automatic Batching)

复制代码
// React 17 及之前:在事件处理程序外部的更新不会批处理
setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React 17 会渲染两次
}, 1000);

// React 18:所有更新都会自动批处理
setTimeout(() => {
  setCount(c => c + 1);
  setFlag(f => !f);
  // React 18 只会渲染一次
}, 1000);

// 如果需要同步更新,可以使用 flushSync
import { flushSync } from 'react-dom';

function handleClick() {
  flushSync(() => {
    setCount(c => c + 1);
  });
  // DOM 已更新
  
  flushSync(() => {
    setFlag(f => !f);
  });
  // DOM 再次更新
}

总结

React 作为一个强大而灵活的库,已经成为了现代 Web 开发的标准工具。通过本文的全面介绍,我们从基础概念到高级应用,从最佳实践到最新特性,系统地探索了 React 的方方面面。

相关推荐
家里有只小肥猫2 小时前
react 初体验
react.js
小龙在山东2 小时前
VS Code 使用 Chrome DevTools MCP 实现浏览器自动化
前端·自动化·chrome devtools
悠哉摸鱼大王2 小时前
多模态场景下tts功能实现
前端·javascript
东华帝君2 小时前
__proto__对比prototype
前端
夜晓码农2 小时前
VSCode Web版本安装
前端·ide·vscode
初出茅庐的3 小时前
hooks&&状态管理&&业务管理
前端·javascript·vue.js
aricvvang3 小时前
一行 Promise.all 争议:数据库查询并行真的没用?我和同事吵赢了!!!
javascript·后端·node.js
三掌柜6663 小时前
2025三掌柜赠书活动第三十五期 AI辅助React Web应用开发实践:基于React 19和GitHub Copilot
前端·人工智能·react.js
YH丶浩3 小时前
vue自定义数字滚动插件
开发语言·前端·javascript·vue