Skip to content

React 介绍与核心概念:构建现代用户界面的 JavaScript 库

什么是 React?

React 是由 Facebook(现在的 Meta)开发的 JavaScript 库,用于构建用户界面,特别是单页应用(SPA)。它引入了组件化虚拟 DOM声明式编程等革命性概念,彻底改变了前端开发的方式。

想象一下建造一座乐高城市:

  • 传统 Web 开发:像是在一整块粘土上雕刻,修改一处可能影响整体
  • React 开发:像是用乐高积木搭建,每个组件都是独立的积木,可以自由组合和替换
jsx
// React 组件的基本结构
function WelcomeCard({ name, age }) {
  return (
    <div className="welcome-card">
      <h2>欢迎,{name}!</h2>
      <p>您今年 {age} 岁了。</p>
      <button onClick={() => alert(`你好,${name}!`)}>点击问候</button>
    </div>
  );
}

// 使用组件
function App() {
  return (
    <div className="app">
      <WelcomeCard name="张三" age={25} />
      <WelcomeCard name="李四" age={30} />
    </div>
  );
}

React 的诞生故事

Facebook 的痛点(2010 年代初)

在 React 出现之前,Facebook 面临着严重的前端开发挑战:

问题 1:复杂的 UI 状态管理

javascript
// 传统DOM操作的问题
var likeButton = document.getElementById("like-button");
var likeCount = 0;

likeButton.onclick = function () {
  likeCount++;
  likeButton.textContent = "点赞 (" + likeCount + ")";

  // 需要手动更新所有相关DOM
  document.getElementById("like-count").textContent = likeCount;
  document.getElementById("like-status").textContent =
    likeCount > 0 ? "有人点赞了!" : "还没有点赞";
};

// 每次状态变化都要手动更新多个地方

问题 2:代码重复和维护困难

javascript
// 相似功能的重复代码
function createProfileCard(user) {
  var card = document.createElement("div");
  card.className = "profile-card";

  var avatar = document.createElement("img");
  avatar.src = user.avatar;
  card.appendChild(avatar);

  var name = document.createElement("h3");
  name.textContent = user.name;
  card.appendChild(name);

  var bio = document.createElement("p");
  bio.textContent = user.bio;
  card.appendChild(bio);

  // 50行类似的DOM操作代码...
}

function createFriendCard(friend) {
  // 又是50行几乎相同的代码...
}

问题 3:数据流不清晰 当应用变得复杂时,很难追踪数据是如何在应用中流动的,一个小的修改可能导致意外的副作用。

Jordan Walke 的创新(2011-2013)

2011 年,Facebook 工程师 Jordan Walke 开始着手解决这些问题。他引入了几个革命性的概念:

1. 函数式 UI 编程

javascript
// Jordan 的早期想法:UI = f(state)
function renderUI(state) {
  return {
    type: "div",
    props: { className: "app" },
    children: [
      {
        type: "h1",
        props: { children: `计数: ${state.count}` },
      },
      {
        type: "button",
        props: {
          children: "增加",
          onClick: () => updateState({ count: state.count + 1 }),
        },
      },
    ],
  };
}

2. 虚拟 DOM 的概念

javascript
// 虚拟DOM的核心思想
// 1. 在内存中构建虚拟DOM树
var oldVirtualDOM = {
  type: "div",
  props: { className: "container" },
  children: [
    { type: "h1", props: { children: "旧标题" } },
    { type: "p", props: { children: "旧内容" } },
  ],
};

// 2. 创建新的虚拟DOM
var newVirtualDOM = {
  type: "div",
  props: { className: "container" },
  children: [
    { type: "h1", props: { children: "新标题" } }, // 变化
    { type: "p", props: { children: "旧内容" } }, // 未变化
  ],
};

// 3. 比较差异,只更新变化的部分
var diff = {
  type: "UPDATE",
  path: [0, "props", "children"],
  newValue: "新标题",
};

3. JSX 语法糖

javascript
// 早期React:使用JavaScript对象描述UI
React.createElement(
  "div",
  { className: "greeting" },
  React.createElement("h1", null, "Hello, ", this.props.name)
);

// JSX:更直观的语法糖
<div className="greeting">
  <h1>Hello, {this.props.name}</h1>
</div>;

// JSX会被编译成上面的JavaScript代码

React 的公开发布(2013-2015)

2013 年 5 月:React 在 Facebook 的 F8 大会上首次公开发布

2015 年 7 月:React Native 发布,将 React 的理念带到了移动端

javascript
// React Native:相同的组件理念,不同的平台
import React from "react";
import { View, Text, TouchableOpacity } from "react-native";

function WelcomeButton({ name, onPress }) {
  return (
    <TouchableOpacity onPress={onPress}>
      <View style={{ padding: 20, backgroundColor: "#007AFF" }}>
        <Text style={{ color: "white", fontSize: 18 }}>欢迎,{name}!</Text>
      </View>
    </TouchableOpacity>
  );
}

React 的核心特性

1. 组件化(Component-Based)

组件是 React 应用的基本构建块,每个组件都是独立的、可复用的 UI 片段:

jsx
// 函数组件(现代推荐的方式)
function UserCard({ user }) {
  return (
    <div className="user-card">
      <img src={user.avatar} alt={user.name} />
      <div className="user-info">
        <h3>{user.name}</h3>
        <p>{user.email}</p>
        <p className="user-role">{user.role}</p>
      </div>
      <div className="user-actions">
        <button onClick={() => followUser(user.id)}>关注</button>
        <button onClick={() => messageUser(user.id)}>发消息</button>
      </div>
    </div>
  );
}

// 使用组件
function App() {
  const users = [
    {
      id: 1,
      name: "张三",
      email: "[email protected]",
      role: "前端工程师",
      avatar: "avatar1.jpg",
    },
    {
      id: 2,
      name: "李四",
      email: "[email protected]",
      role: "后端工程师",
      avatar: "avatar2.jpg",
    },
  ];

  return (
    <div className="user-list">
      {users.map((user) => (
        <UserCard key={user.id} user={user} />
      ))}
    </div>
  );
}

2. 声明式编程(Declarative)

React 采用声明式编程,你只需要描述 UI 应该是什么样子,而不需要关心如何实现:

jsx
// 声明式:描述你想要什么
function TodoList({ todos, onDeleteTodo, onToggleTodo }) {
  return (
    <ul className="todo-list">
      {todos
        .filter((todo) => !todo.completed) // 只显示未完成的
        .sort((a, b) => a.priority - b.priority) // 按优先级排序
        .map((todo) => (
          <TodoItem
            key={todo.id}
            todo={todo}
            onDelete={onDeleteTodo}
            onToggle={onToggleTodo}
          />
        ))}
    </ul>
  );
}

// 对比命令式:描述如何实现
function renderTodoList(todos) {
  var ul = document.createElement("ul");
  ul.className = "todo-list";

  // 手动过滤
  var incompleteTodos = todos.filter(function (todo) {
    return !todo.completed;
  });

  // 手动排序
  incompleteTodos.sort(function (a, b) {
    return a.priority - b.priority;
  });

  // 手动创建和添加元素
  incompleteTodos.forEach(function (todo) {
    var li = document.createElement("li");
    li.textContent = todo.text;

    var deleteBtn = document.createElement("button");
    deleteBtn.textContent = "删除";
    deleteBtn.onclick = function () {
      deleteTodo(todo.id);
    };

    li.appendChild(deleteBtn);
    ul.appendChild(li);
  });

  return ul;
}

3. 虚拟 DOM(Virtual DOM)

虚拟 DOM 是 React 性能优化的核心技术:

jsx
// 虚拟DOM的工作流程

// 1. 状态变化时,React创建新的虚拟DOM
function Counter() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>计数: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        增加
      </button>
    </div>
  );
}

// 2. React比较新旧虚拟DOM的差异
// 旧虚拟DOM
{
  type: 'div',
  props: {},
  children: [
    { type: 'p', props: { children: '计数: 0' } },
    { type: 'button', props: { children: '增加', onClick: [Function] } }
  ]
}

// 新虚拟DOM
{
  type: 'div',
  props: {},
  children: [
    { type: 'p', props: { children: '计数: 1' } }, // 只有这里变化
    { type: 'button', props: { children: '增加', onClick: [Function] } }
  ]
}

// 3. 计算出最小的DOM更新操作
// 只更新 <p> 元素的内容,而不是重新渲染整个组件

4. 单向数据流(Unidirectional Data Flow)

React 采用单向数据流,使得数据流动更加可预测:

jsx
// 父组件
function ShoppingCart() {
  const [items, setItems] = useState([
    { id: 1, name: "React教程", price: 99, quantity: 1 },
    { id: 2, name: "JavaScript高级", price: 129, quantity: 2 },
  ]);

  const updateQuantity = (id, newQuantity) => {
    setItems(
      items.map((item) =>
        item.id === id ? { ...item, quantity: newQuantity } : item
      )
    );
  };

  const removeItem = (id) => {
    setItems(items.filter((item) => item.id !== id));
  };

  const total = items.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  );

  return (
    <div className="shopping-cart">
      <h2>购物车</h2>
      {items.map((item) => (
        <CartItem
          key={item.id}
          item={item}
          onUpdateQuantity={updateQuantity}
          onRemove={removeItem}
        />
      ))}
      <div className="cart-total">总计: ¥{total}</div>
    </div>
  );
}

// 子组件只能接收props,不能直接修改父组件状态
function CartItem({ item, onUpdateQuantity, onRemove }) {
  return (
    <div className="cart-item">
      <h4>{item.name}</h4>
      <p>¥{item.price}</p>
      <div className="quantity-controls">
        <button
          onClick={() => onUpdateQuantity(item.id, item.quantity - 1)}
          disabled={item.quantity <= 1}
        >
          -
        </button>
        <span>{item.quantity}</span>
        <button onClick={() => onUpdateQuantity(item.id, item.quantity + 1)}>
          +
        </button>
        <button onClick={() => onRemove(item.id)}>删除</button>
      </div>
    </div>
  );
}

React 的工作原理

1. JSX 转换过程

JSX 语法会被 Babel 等工具转换为 JavaScript 代码:

jsx
// JSX代码
function App() {
  const user = { name: "张三", age: 25 };

  return (
    <div className="app">
      <h1>欢迎, {user.name}!</h1>
      <p>您今年 {user.age} 岁了。</p>
      {user.age >= 18 && <p>您是成年人</p>}
    </div>
  );
}

// 转换后的JavaScript代码
function App() {
  const user = { name: "张三", age: 25 };

  return React.createElement(
    "div",
    { className: "app" },
    React.createElement("h1", null, "欢迎, ", user.name, "!"),
    React.createElement("p", null, "您今年 ", user.age, " 岁了。"),
    user.age >= 18 && React.createElement("p", null, "您是成年人")
  );
}

// 更复杂示例的转换
function UserList({ users }) {
  return (
    <ul className="user-list">
      {users.map((user) => (
        <li key={user.id} className="user-item">
          <strong>{user.name}</strong>: {user.email}
        </li>
      ))}
    </ul>
  );
}

// 转换后
function UserList({ users }) {
  return React.createElement(
    "ul",
    { className: "user-list" },
    users.map((user) =>
      React.createElement(
        "li",
        { key: user.id, className: "user-item" },
        React.createElement("strong", null, user.name),
        ": ",
        user.email
      )
    )
  );
}

2. 组件生命周期(函数组件)

现代 React 使用 Hooks 来管理组件的生命周期:

jsx
import { useState, useEffect, useRef, memo } from "react";

// 使用Hooks的函数组件生命周期
function UserProfile({ userId }) {
  // 状态管理
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  // 副作用:类似componentDidMount和componentDidUpdate
  useEffect(() => {
    async function fetchUser() {
      try {
        setLoading(true);
        const response = await fetch(`/api/users/${userId}`);
        const userData = await response.json();
        setUser(userData);
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    }

    fetchUser();
  }, [userId]); // 依赖数组:userId变化时重新执行

  // 清理函数:类似componentWillUnmount
  useEffect(() => {
    const timer = setInterval(() => {
      console.log("用户数据定时刷新");
    }, 30000);

    return () => clearInterval(timer); // 组件卸载时清理定时器
  }, []);

  // Ref:直接访问DOM元素
  const avatarRef = useRef(null);

  useEffect(() => {
    if (avatarRef.current) {
      // 可以直接操作DOM元素
      avatarRef.current.classList.add("fade-in");
    }
  }, [user]);

  // 条件渲染
  if (loading) return <div>加载中...</div>;
  if (error) return <div>错误: {error}</div>;
  if (!user) return <div>用户不存在</div>;

  return (
    <div className="user-profile">
      <img ref={avatarRef} src={user.avatar} alt={`${user.name}的头像`} />
      <h2>{user.name}</h2>
      <p>{user.bio}</p>
      <div className="user-stats">
        <span>关注: {user.following}</span>
        <span>粉丝: {user.followers}</span>
      </div>
    </div>
  );
}

// 使用memo进行性能优化:避免不必要的重新渲染
export default memo(UserProfile);

3. 状态更新和重新渲染

jsx
function Counter() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("");
  const [history, setHistory] = useState([]);

  // 状态更新会触发重新渲染
  const increment = () => {
    setCount((prevCount) => prevCount + 1); // 使用函数形式获取最新状态

    // 批量状态更新:React会合并这些更新
    setName("计数器");
    setHistory((prev) => [...prev, `计数变为: ${count + 1}`]);
  };

  const reset = () => {
    setCount(0);
    setName("");
    setHistory([]);
  };

  console.log("组件重新渲染了,count:", count);

  return (
    <div className="counter">
      <h2>计数器: {count}</h2>
      <p>状态: {name}</p>

      <div className="controls">
        <button onClick={increment}>增加</button>
        <button onClick={reset}>重置</button>
      </div>

      <div className="history">
        <h3>历史记录:</h3>
        {history.map((item, index) => (
          <p key={index}>{item}</p>
        ))}
      </div>
    </div>
  );
}

React Hooks 详解

Hooks 是 React 16.8 版本引入的特性,让你在函数组件中使用状态和其他 React 特性:

jsx
import {
  useState,
  useEffect,
  useContext,
  useReducer,
  useCallback,
  useMemo,
  useRef,
} from "react";

// 1. useState - 状态管理
function PersonalInfoForm() {
  const [formData, setFormData] = useState({
    name: "",
    email: "",
    phone: "",
    age: "",
  });

  const [errors, setErrors] = useState({});

  const handleChange = (field, value) => {
    setFormData((prev) => ({
      ...prev,
      [field]: value,
    }));

    // 清除该字段的错误
    if (errors[field]) {
      setErrors((prev) => ({
        ...prev,
        [field]: "",
      }));
    }
  };

  return (
    <form className="personal-info-form">
      <input
        type="text"
        placeholder="姓名"
        value={formData.name}
        onChange={(e) => handleChange("name", e.target.value)}
        className={errors.name ? "error" : ""}
      />
      <input
        type="email"
        placeholder="邮箱"
        value={formData.email}
        onChange={(e) => handleChange("email", e.target.value)}
        className={errors.email ? "error" : ""}
      />
      {/* 更多输入字段... */}
    </form>
  );
}

// 2. useReducer - 复杂状态管理
const todoReducer = (state, action) => {
  switch (action.type) {
    case "ADD_TODO":
      return {
        ...state,
        todos: [
          ...state.todos,
          {
            id: Date.now(),
            text: action.payload,
            completed: false,
          },
        ],
      };
    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 "SET_FILTER":
      return {
        ...state,
        filter: action.payload,
      };
    default:
      return state;
  }
};

function TodoApp() {
  const [state, dispatch] = useReducer(todoReducer, {
    todos: [],
    filter: "ALL", // ALL, ACTIVE, COMPLETED
  });

  const addTodo = (text) => {
    dispatch({ type: "ADD_TODO", payload: text });
  };

  const toggleTodo = (id) => {
    dispatch({ type: "TOGGLE_TODO", payload: id });
  };

  const deleteTodo = (id) => {
    dispatch({ type: "DELETE_TODO", payload: id });
  };

  const setFilter = (filter) => {
    dispatch({ type: "SET_FILTER", payload: filter });
  };

  const filteredTodos = state.todos.filter((todo) => {
    switch (state.filter) {
      case "ACTIVE":
        return !todo.completed;
      case "COMPLETED":
        return todo.completed;
      default:
        return true;
    }
  });

  return (
    <div className="todo-app">
      <TodoInput onAdd={addTodo} />
      <TodoFilter filter={state.filter} onFilterChange={setFilter} />
      <TodoList
        todos={filteredTodos}
        onToggle={toggleTodo}
        onDelete={deleteTodo}
      />
    </div>
  );
}

// 3. useContext - 跨组件状态共享
const ThemeContext = React.createContext();
const UserContext = React.createContext();

function App() {
  const [theme, setTheme] = useState("light");
  const [user, setUser] = useState(null);

  return (
    <ThemeContext.Provider value={{ theme, setTheme }}>
      <UserContext.Provider value={{ user, setUser }}>
        <MainApp />
      </UserContext.Provider>
    </ThemeContext.Provider>
  );
}

function Header() {
  const { theme, setTheme } = useContext(ThemeContext);
  const { user } = useContext(UserContext);

  return (
    <header className={`header ${theme}`}>
      <h1>我的应用</h1>
      <div className="header-actions">
        {user ? <span>欢迎, {user.name}</span> : <button>登录</button>}
        <button onClick={() => setTheme(theme === "light" ? "dark" : "light")}>
          切换主题
        </button>
      </div>
    </header>
  );
}

// 4. useCallback - 性能优化
function ParentComponent() {
  const [count, setCount] = useState(0);
  const [name, setName] = useState("");

  // 使用useCallback避免每次重新创建函数
  const handleIncrement = useCallback(() => {
    setCount((prevCount) => prevCount + 1);
  }, []); // 空依赖数组意味着函数永远不会重新创建

  const handleNameChange = useCallback((value) => {
    setName(value);
  }, []); // 同样,函数永远不会重新创建

  return (
    <div>
      <ChildButton onClick={handleIncrement} />
      <ChildInput value={name} onChange={handleNameChange} />
      <p>计数: {count}</p>
    </div>
  );
}

// 5. useMemo - 计算结果缓存
function ExpensiveCalculation({ data }) {
  // 使用useMemo缓存计算结果
  const expensiveValue = useMemo(() => {
    console.log("执行复杂计算...");
    return data.reduce(
      (sum, num) => sum + Math.sqrt(num) * Math.log(num + 1),
      0
    );
  }, [data]); // 只有当data变化时才重新计算

  return (
    <div>
      <p>计算结果: {expensiveValue}</p>
      <p>数据长度: {data.length}</p>
    </div>
  );
}

// 6. useRef - 引用DOM元素或持久值
function InputWithFocus() {
  const inputRef = useRef(null);
  const renderCountRef = useRef(0);

  renderCountRef.current += 1; // 不会触发重新渲染

  const focusInput = () => {
    inputRef.current?.focus();
  };

  return (
    <div>
      <input ref={inputRef} type="text" placeholder="点击按钮聚焦" />
      <button onClick={focusInput}>聚焦输入框</button>
      <p>组件渲染次数: {renderCountRef.current}</p>
    </div>
  );
}

React 的现代应用场景

1. 单页应用(SPA)

jsx
import {
  BrowserRouter as Router,
  Routes,
  Route,
  Link,
  useParams,
} from "react-router-dom";

// 路由配置
function App() {
  return (
    <Router>
      <div className="app">
        <Navigation />
        <Routes>
          <Route path="/" element={<HomePage />} />
          <Route path="/about" element={<AboutPage />} />
          <Route path="/products" element={<ProductsPage />} />
          <Route path="/products/:id" element={<ProductDetail />} />
          <Route path="/contact" element={<ContactPage />} />
        </Routes>
        <Footer />
      </div>
    </Router>
  );
}

// 导航组件
function Navigation() {
  return (
    <nav className="navigation">
      <Link to="/" className="nav-brand">
        我的网站
      </Link>
      <div className="nav-links">
        <Link to="/">首页</Link>
        <Link to="/about">关于</Link>
        <Link to="/products">产品</Link>
        <Link to="/contact">联系</Link>
      </div>
    </nav>
  );
}

// 动态路由参数
function ProductDetail() {
  const { id } = useParams();
  const [product, setProduct] = useState(null);

  useEffect(() => {
    fetch(`/api/products/${id}`)
      .then((res) => res.json())
      .then((data) => setProduct(data));
  }, [id]);

  if (!product) return <div>加载中...</div>;

  return (
    <div className="product-detail">
      <h1>{product.name}</h1>
      <p>价格: ¥{product.price}</p>
      <p>{product.description}</p>
    </div>
  );
}

2. 实时数据应用

jsx
import { useState, useEffect } from "react";

function ChatRoom() {
  const [messages, setMessages] = useState([]);
  const [newMessage, setNewMessage] = useState("");
  const [isConnected, setIsConnected] = useState(false);

  // WebSocket连接
  useEffect(() => {
    const ws = new WebSocket("ws://localhost:8080/chat");

    ws.onopen = () => {
      setIsConnected(true);
      console.log("连接到聊天室");
    };

    ws.onmessage = (event) => {
      const message = JSON.parse(event.data);
      setMessages((prev) => [...prev, message]);
    };

    ws.onclose = () => {
      setIsConnected(false);
      console.log("断开连接");
    };

    return () => ws.close();
  }, []);

  const sendMessage = () => {
    if (newMessage.trim() && isConnected) {
      const message = {
        id: Date.now(),
        text: newMessage,
        timestamp: new Date().toISOString(),
        user: "当前用户",
      };

      // 发送消息到服务器
      ws.send(JSON.stringify(message));
      setNewMessage("");
    }
  };

  return (
    <div className="chat-room">
      <div className="chat-header">
        <h2>聊天室</h2>
        <span
          className={`connection-status ${
            isConnected ? "connected" : "disconnected"
          }`}
        >
          {isConnected ? "已连接" : "连接中..."}
        </span>
      </div>

      <div className="messages-container">
        {messages.map((message) => (
          <div key={message.id} className="message">
            <span className="message-user">{message.user}:</span>
            <span className="message-text">{message.text}</span>
            <span className="message-time">
              {new Date(message.timestamp).toLocaleTimeString()}
            </span>
          </div>
        ))}
      </div>

      <div className="message-input">
        <input
          type="text"
          value={newMessage}
          onChange={(e) => setNewMessage(e.target.value)}
          onKeyPress={(e) => e.key === "Enter" && sendMessage()}
          placeholder="输入消息..."
          disabled={!isConnected}
        />
        <button
          onClick={sendMessage}
          disabled={!isConnected || !newMessage.trim()}
        >
          发送
        </button>
      </div>
    </div>
  );
}

3. 表单处理

jsx
import { useState, useEffect } from "react";

function UserRegistrationForm() {
  const [formData, setFormData] = useState({
    username: "",
    email: "",
    password: "",
    confirmPassword: "",
    age: "",
    interests: [],
    country: "",
    agreement: false,
  });

  const [errors, setErrors] = useState({});
  const [isSubmitting, setIsSubmitting] = useState(false);

  // 表单验证
  const validateForm = () => {
    const newErrors = {};

    if (!formData.username.trim()) {
      newErrors.username = "用户名不能为空";
    } else if (formData.username.length < 3) {
      newErrors.username = "用户名至少3个字符";
    }

    if (!formData.email.trim()) {
      newErrors.email = "邮箱不能为空";
    } else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
      newErrors.email = "邮箱格式不正确";
    }

    if (!formData.password) {
      newErrors.password = "密码不能为空";
    } else if (formData.password.length < 8) {
      newErrors.password = "密码至少8个字符";
    }

    if (formData.password !== formData.confirmPassword) {
      newErrors.confirmPassword = "两次输入的密码不一致";
    }

    if (!formData.age) {
      newErrors.age = "请选择年龄";
    } else if (formData.age < 18) {
      newErrors.age = "必须年满18岁";
    }

    if (formData.interests.length === 0) {
      newErrors.interests = "请至少选择一个兴趣";
    }

    if (!formData.country) {
      newErrors.country = "请选择国家";
    }

    if (!formData.agreement) {
      newErrors.agreement = "必须同意服务条款";
    }

    setErrors(newErrors);
    return Object.keys(newErrors).length === 0;
  };

  // 处理表单提交
  const handleSubmit = async (e) => {
    e.preventDefault();

    if (!validateForm()) {
      return;
    }

    setIsSubmitting(true);

    try {
      const response = await fetch("/api/register", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify(formData),
      });

      if (response.ok) {
        // 注册成功
        alert("注册成功!");
        // 重置表单
        setFormData({
          username: "",
          email: "",
          password: "",
          confirmPassword: "",
          age: "",
          interests: [],
          country: "",
          agreement: false,
        });
      } else {
        const errorData = await response.json();
        setErrors({ general: errorData.message || "注册失败" });
      }
    } catch (error) {
      setErrors({ general: "网络错误,请稍后重试" });
    } finally {
      setIsSubmitting(false);
    }
  };

  // 处理输入变化
  const handleInputChange = (field, value) => {
    setFormData((prev) => ({
      ...prev,
      [field]: value,
    }));

    // 清除该字段的错误
    if (errors[field]) {
      setErrors((prev) => ({
        ...prev,
        [field]: "",
      }));
    }
  };

  // 处理兴趣选择
  const handleInterestChange = (interest) => {
    setFormData((prev) => ({
      ...prev,
      interests: prev.interests.includes(interest)
        ? prev.interests.filter((i) => i !== interest)
        : [...prev.interests, interest],
    }));
  };

  return (
    <form onSubmit={handleSubmit} className="registration-form">
      <h2>用户注册</h2>

      {errors.general && <div className="error-message">{errors.general}</div>}

      <div className="form-group">
        <label htmlFor="username">用户名</label>
        <input
          type="text"
          id="username"
          value={formData.username}
          onChange={(e) => handleInputChange("username", e.target.value)}
          className={errors.username ? "error" : ""}
        />
        {errors.username && (
          <span className="error-text">{errors.username}</span>
        )}
      </div>

      <div className="form-group">
        <label htmlFor="email">邮箱</label>
        <input
          type="email"
          id="email"
          value={formData.email}
          onChange={(e) => handleInputChange("email", e.target.value)}
          className={errors.email ? "error" : ""}
        />
        {errors.email && <span className="error-text">{errors.email}</span>}
      </div>

      {/* 更多表单字段... */}

      <button type="submit" disabled={isSubmitting}>
        {isSubmitting ? "注册中..." : "注册"}
      </button>
    </form>
  );
}

为什么选择 React?

1. 丰富的生态系统

  • 大量的第三方库:React Router、Redux、Material-UI、Ant Design 等
  • 活跃的社区:Stack Overflow、GitHub、Reddit 等平台有丰富的资源
  • 企业级支持:Facebook、Netflix、Airbnb 等大公司都在使用

2. 优秀的开发体验

jsx
// 开发工具和调试
- React Developer Tools(浏览器扩展)
- TypeScript 完整支持
- Hot Module Replacement(热模块替换)
- 丰富的错误提示和调试信息

3. 性能优化能力

jsx
// 多种性能优化方案
- useMemo 和 useCallback 缓存计算和函数
- React.memo 避免不必要的组件重渲染
- lazy 和 Suspense 实现代码分割
- 虚拟滚动处理大数据列表
- Concurrent Mode 提升渲染性能

4. 学习价值和就业机会

React 是目前最受欢迎的前端框架之一,掌握 React 可以为开发者提供很好的就业机会和发展前景。

总结

React 通过引入组件化、虚拟 DOM 和声明式编程等概念,彻底改变了前端开发的方式。它不仅是一个库,更是一种新的思考和构建用户界面的方法论。

本节要点回顾

  • React 是 Facebook 开发的用于构建用户界面的 JavaScript 库
  • 核心特性包括组件化、声明式编程、虚拟 DOM 和单向数据流
  • JSX 让 HTML-like 语法在 JavaScript 中成为可能
  • Hooks 让函数组件拥有状态管理和其他 React 特性
  • React 生态系统丰富,开发体验优秀,就业前景广阔
  • 理解 React 的工作原理和最佳实践是现代前端开发的必备技能

React 的设计理念影响了整个前端行业的发展,许多其他框架都借鉴了它的核心概念。在接下来的学习中,我们将深入探讨 React 的各个方面,包括组件开发、状态管理、性能优化、测试策略等,帮助你成为一名优秀的 React 开发者。

上次更新时间: