零基础全栈 React 入门(三):状态管理与事件处理

20 阅读3分钟

🚀 零基础全栈 React 入门(三):状态管理与事件处理

一、事件处理基础

1.1 React 事件绑定

React 事件采用驼峰命名法,事件处理函数作为属性传递。

function Button() {
  const handleClick = () => {
    console.log('按钮被点击了!');
  };

  return <button onClick={handleClick}>点击我</button>;
}

1.2 事件对象

function Button() {
  const handleClick = (e) => {
    e.preventDefault();  // 阻止默认行为
    console.log('事件对象:', e);
  };

  return <button onClick={handleClick}>点击</button>;
}

1.3 向事件处理函数传递参数

方式一:使用箭头函数

function ListItem({ item }) {
  const handleClick = () => {
    console.log('点击了:', item.name);
  };

  return <li onClick={handleClick}>{item.name}</li>;
}

方式二:使用 bind

function ListItem({ item }) {
  const handleClick = (item) => {
    console.log('点击了:', item.name);
  };

  return <li onClick={handleClick.bind(this, item)}>{item.name}</li>;
}

二、状态管理进阶

2.1 复杂状态管理

import { useState } from 'react';

function UserProfile() {
  const [user, setUser] = useState({
    name: '小明',
    age: 25,
    isOnline: true
  });

  const toggleOnline = () => {
    // 正确:使用展开运算符更新对象
    setUser(prev => ({
      ...prev,
      isOnline: !prev.isOnline
    }));
  };

  return (
    <div>
      <h2>{user.name}</h2>
      <p>年龄: {user.age}</p>
      <p>状态: {user.isOnline ? '在线' : '离线'}</p>
      <button onClick={toggleOnline}>切换状态</button>
    </div>
  );
}

2.2 数组状态管理

import { useState } from 'react';

function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: '学习 React', completed: false },
    { id: 2, text: '完成作业', completed: true }
  ]);

  // 添加待办事项
  const addTodo = (text) => {
    const newTodo = {
      id: Date.now(),
      text,
      completed: false
    };
    setTodos(prev => [...prev, newTodo]);
  };

  // 切换完成状态
  const toggleTodo = (id) => {
    setTodos(prev => prev.map(todo => 
      todo.id === id ? { ...todo, completed: !todo.completed } : todo
    ));
  };

  return (
    <div>
      <ul>
        {todos.map(todo => (
          <li 
            key={todo.id}
            onClick={() => toggleTodo(todo.id)}
            style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}
          >
            {todo.text}
          </li>
        ))}
      </ul>
    </div>
  );
}

三、表单处理

3.1 受控组件

表单元素的值由 React 状态控制:

import { useState } from 'react';

function LoginForm() {
  const [username, setUsername] = useState('');
  const [password, setPassword] = useState('');

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('用户名:', username);
    console.log('密码:', password);
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label>用户名:</label>
        <input
          type="text"
          value={username}
          onChange={(e) => setUsername(e.target.value)}
        />
      </div>
      <div>
        <label>密码:</label>
        <input
          type="password"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
        />
      </div>
      <button type="submit">登录</button>
    </form>
  );
}

3.2 多个表单字段优化

import { useState } from 'react';

function RegisterForm() {
  const [formData, setFormData] = useState({
    username: '',
    email: '',
    password: ''
  });

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

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log('表单数据:', formData);
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        name="username"
        value={formData.username}
        onChange={handleChange}
        placeholder="用户名"
      />
      <input
        type="email"
        name="email"
        value={formData.email}
        onChange={handleChange}
        placeholder="邮箱"
      />
      <input
        type="password"
        name="password"
        value={formData.password}
        onChange={handleChange}
        placeholder="密码"
      />
      <button type="submit">注册</button>
    </form>
  );
}

四、条件渲染进阶

4.1 元素变量

function Greeting({ isLoggedIn, user }) {
  let greeting;
  
  if (isLoggedIn) {
    greeting = <h1>欢迎回来, {user.name}!</h1>;
  } else {
    greeting = <h1>请先登录</h1>;
  }

  return <div>{greeting}</div>;
}

4.2 组件级条件渲染

function LoginButton({ onLogin }) {
  return <button onClick={onLogin}>登录</button>;
}

function LogoutButton({ onLogout }) {
  return <button onClick={onLogout}>退出登录</button>;
}

function AuthButton({ isLoggedIn, onLogin, onLogout }) {
  if (isLoggedIn) {
    return <LogoutButton onLogout={onLogout} />;
  }
  return <LoginButton onLogin={onLogin} />;
}

五、列表渲染进阶

5.1 Key 的重要性

// ❌ 错误:使用 index 作为 key(不推荐)
const list = items.map((item, index) => (
  <li key={index}>{item.name}</li>
));

// ✅ 正确:使用唯一 ID 作为 key
const list = items.map(item => (
  <li key={item.id}>{item.name}</li>
));

5.2 列表操作示例

import { useState } from 'react';

function ShoppingList() {
  const [items, setItems] = useState([
    { id: 1, name: '牛奶', quantity: 2 },
    { id: 2, name: '面包', quantity: 1 }
  ]);

  // 删除项
  const removeItem = (id) => {
    setItems(prev => prev.filter(item => item.id !== id));
  };

  // 更新数量
  const updateQuantity = (id, delta) => {
    setItems(prev => prev.map(item => 
      item.id === id 
        ? { ...item, quantity: Math.max(0, item.quantity + delta) }
        : item
    ));
  };

  return (
    <ul>
      {items.map(item => (
        <li key={item.id}>
          <span>{item.name}</span>
          <button onClick={() => updateQuantity(item.id, -1)}>-</button>
          <span>{item.quantity}</span>
          <button onClick={() => updateQuantity(item.id, 1)}>+</button>
          <button onClick={() => removeItem(item.id)}>删除</button>
        </li>
      ))}
    </ul>
  );
}

六、状态提升

6.1 什么是状态提升

当多个组件需要共享状态时,将状态提升到它们的共同父组件中。

// 子组件:显示温度
function TemperatureDisplay({ temperature }) {
  return <p>当前温度: {temperature}°C</p>;
}

// 子组件:温度控制
function TemperatureControl({ temperature, onTemperatureChange }) {
  return (
    <div>
      <button onClick={() => onTemperatureChange(temperature - 1)}>-</button>
      <span>{temperature}</span>
      <button onClick={() => onTemperatureChange(temperature + 1)}>+</button>
    </div>
  );
}

// 父组件:管理状态
function Thermostat() {
  const [temperature, setTemperature] = useState(20);

  return (
    <div>
      <TemperatureDisplay temperature={temperature} />
      <TemperatureControl 
        temperature={temperature} 
        onTemperatureChange={setTemperature} 
      />
    </div>
  );
}

七、常见错误

7.1 直接修改状态

错误:

const [count, setCount] = useState(0);

// ❌ 直接修改状态
count = count + 1;

正确:

// ✅ 使用 setState
setCount(count + 1);

7..2 忘记阻止默认行为

错误:

function handleSubmit(e) {
  // ❌ 表单会重新加载页面
  console.log('提交');
}

正确:

function handleSubmit(e) {
  e.preventDefault();  // ✅ 阻止默认行为
  console.log('提交');
}

7.3 事件处理函数中的 this 问题

错误:

// ❌ 在类组件中,this 指向问题
<button onClick={this.handleClick}>点击</button>

正确:

// ✅ 使用箭头函数
<button onClick={() => this.handleClick()}>点击</button>

// ✅ 或者在 constructor 中绑定
constructor(props) {
  super(props);
  this.handleClick = this.handleClick.bind(this);
}

八、总结与练习

8.1 本章要点

  1. ✅ React 事件使用驼峰命名
  2. ✅ 使用 useState 管理组件状态
  3. ✅ 状态更新必须使用 setState 函数
  4. ✅ 表单处理使用受控组件模式
  5. ✅ 列表渲染必须添加 key 属性
  6. ✅ 状态提升用于组件间共享状态

8.2 课后练习

  1. 创建一个待办事项应用(添加、删除、完成状态切换)
  2. 创建一个用户注册表单(包含用户名、邮箱、密码)
  3. 创建一个计数器组件,支持加减和重置功能

📌 系列文章导航:

  • 第一篇:React 入门前置与环境搭建
  • 第二篇:JSX 语法与组件基础
  • 第三篇:状态管理与事件处理(当前)
  • 第四篇:React Router 路由配置