React自学 基础一

发布于:2025-07-04 ⋅ 阅读:(18) ⋅ 点赞:(0)

React基础

React 是一个由 Facebook(现 Meta)开发并维护的、开源JavaScript 库,主要用于 构建用户界面(UI),尤其是单页面应用程序中的动态、交互式界面。

image-20250612192645692

简单示例:

import React, { useState } from 'react';

function Counter() {
  const [count, setCount] = useState(0); // 使用 React Hooks 管理状态

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

一、创建React工程

创建 React 项目主要有三种主流方式,根据你的需求(如开发效率、性能要求、是否需要服务端渲染等),可灵活选择以下任一方法:


🚀 一、使用 Vite(推荐:速度快、现代化)

Vite 是当前最受欢迎的 React 项目初始化工具,启动速度和热更新远超传统工具,适合绝大多数新项目。
操作步骤:

  1. 确保已安装 Node.js(建议 v16+)。

  2. 在终端执行以下命令:

    npm create vite@latest my-vite-app -- --template react
    
  3. 进入项目目录并安装依赖:

    cd my-vite-app
    npm install
    
  4. 启动开发服务器:

    npm run dev
    
    • 访问 http://localhost:5173 查看项目。
  5. 在Chrome浏览器中添加组件

    React 因其高效、灵活和强大的社区支持,成为现代前端开发的主流选择之一。

    在Chrome浏览器中添加组件

    image-20250628204026748

    image-20250628204129958


⚙️ 二、使用 Next.js(适合服务端渲染/静态站点)

若项目需服务端渲染(SSR)、静态生成(SSG)或强 SEO 支持(如博客、电商),Next.js 是最佳选择。
操作步骤:

  1. 创建项目:
    npx create-next-app@latest my-next-app
    
  2. 进入目录并启动:
    cd my-next-app
    npm run dev
    
    • 访问 http://localhost:3000

🧩 三、使用 create-react-app(传统方式,已逐渐淘汰)

虽然官方仍在维护,但启动慢、配置隐藏,仅适合学习或兼容旧项目。
操作步骤:

  1. 全局安装(可选):
    npm install -g create-react-app
    
  2. 创建项目:
    npx create-react-app my-app
    
  3. 启动项目:
    cd my-app
    npm start
    
    • 访问 http://localhost:3000

📊 三种方式对比与适用场景

工具 优势 适用场景 启动命令
Vite ⚡ 超快启动、轻量、配置灵活 新项目、开发效率优先 npm run dev
Next.js 🌐 服务端渲染、SEO 友好、全栈能力 博客、电商、需 SEO 的应用 npm run dev
CRA 🧠 零配置、官方维护(但慢) 学习 React 基础 npm start

常见问题解答(FAQ)

  • Q1:是否需要全局安装工具?
    不需要!现代工具(如 Vite、Next.js)通过 npx 直接运行最新版本,避免全局依赖冲突。
  • Q2:项目名称能否用大写字母?
    不能!所有工具均要求项目名全小写(如 my-app)。
  • Q3:如何修改默认端口?
    package.json 的启动命令后添加 --port=新端口号,例如:
    npm run dev -- --port=4000
    

💎 总结建议

  • 新手/一般项目 → 选 Vite,体验流畅开发;
  • 内容型网站/SEO 关键 → 选 Next.js,直接支持服务端渲染;
  • 学习 React 基础 → 可尝试 create-react-app,但注意其性能局限。

提示:以上命令均需在终端(如 VS Code 终端、命令行)执行。遇到依赖问题可尝试 npm install --force 强制重装。

image-20250612193145686

image-20250612193431658

image-20250630173947566

二、JSX

2.1 关于 JSX

JSX(JavaScript XML) 是一种 JavaScript 的语法扩展(解析工具进行特定解析),由 Facebook 团队为 React 框架设计。它的核心作用是将 HTML 结构直接嵌入 JavaScript 代码中,实现声明式的 UI 构建。

核心特点:
  1. 类 HTML 语法
    允许在 JavaScript 中编写类似 HTML 的标签(例如 <div>Hello</div>)。

  2. JavaScript 表达式嵌入
    { } 包裹动态内容(如变量、函数调用):

    const name = "Alice";
    const element = <h1>Hello, {name}</h1>; // 动态渲染
    
  3. 编译为 JavaScript
    JSX 本身不能被浏览器直接执行,需要通过工具(如 Babel)编译为标准 JavaScript:

    // 编译前 (JSX)
    <button className="btn">Click</button>
    
    // 编译后 (JavaScript)
    React.createElement("button", { className: "btn" }, "Click");
    
  4. 与 React 深度集成
    主要用于构建 React 组件的 UI 结构,但也可用于其他库(如 Vue 的 JSX 支持)。


2.2 关于 Babel (https://babeljs.io/)

Babel 是一个 JavaScript 编译器,它的核心功能是将新版本的 JavaScript 代码(或 JSX 等扩展语法)转换为向后兼容的旧版本 JavaScript,确保代码能在所有浏览器中运行。

关键作用:
  1. 编译 JSX
    将 JSX 语法转换为浏览器可理解的 React.createElement() 调用(如上例所示)。
  2. 转译现代 JavaScript
    将 ES6+ 代码(如箭头函数、async/await、类)转换为 ES5 等旧标准。
    // 编译前 (ES6)
    const list = items.map(item => <li key={item.id}>{item.name}</li>);
    
    // 编译后 (ES5)
    var list = items.map(function(item) {
      return React.createElement("li", { key: item.id }, item.name);
    });
    
  3. 插件化架构
    通过插件支持实验性语法(如装饰器)、类型检查(TypeScript)、代码优化等。

2.3 JSX 与 Babel 的关系

  1. 依赖 Babel 处理
    JSX 必须通过 Babel(或类似工具)编译为纯 JavaScript 才能运行。
  2. 常用预设
    在 React 项目中,通常使用 Babel 预设插件 @babel/preset-react 来编译 JSX。
  3. 工作流程示例
    JSX 代码
    Babel 编译
    标准 JavaScript
    浏览器执行

2.4 如何使用?

  1. 安装 Babel
    npm install @babel/core @babel/preset-react
    
  2. 配置 Babel(创建 .babelrc 文件):
    {
      "presets": ["@babel/preset-react"]
    }
    
  3. 与构建工具集成
    配合 Webpack/Rollup 等工具自动完成编译(例如使用 babel-loader)。

总结

  • JSX 是描述 UI 的语法糖,需编译为 JavaScript。
  • Babel 是编译工具链的核心,处理 JSX 和现代 JavaScript 的兼容性问题。
    官网 babeljs.io 提供了实时编译演示、插件文档和配置指南。

二(支线)、JSX 中使用 JavaScript 表达式

在 JSX 中,你可以使用大括号 {} 来嵌入 JavaScript 表达式。这允许你在 JSX 中动态地插入值、执行计算和渲染逻辑。

2.1 基本用法

const name = 'John Doe';
const element = <h1>Hello, {name}</h1>;

image-20250701154930359

2.2 表达式类型

  1. 变量引用

    const user = { firstName: 'Jane', lastName: 'Doe' };
    const element = <p>{user.firstName} {user.lastName}</p>;
    
  2. 函数调用

   function formatName(user) {
     return `${user.firstName} ${user.lastName}`;
   }
   const element = <h2>{formatName(user)}</h2>;
  1. 算术运算
   const total = 10;
   const count = 3;
   const element = <p>Remaining: {total - count}</p>;
  1. 三元表达式

    const isLoggedIn = true;
    const element = (
      <div>
        {isLoggedIn ? 'Welcome back!' : 'Please sign in'}
      </div>
    );
    
    
    
  2. 数组方法(列表渲染)

 const numbers = [1, 2, 3, 4, 5];
 const listItems = numbers.map((number) => 
   <li key={number}>{number}</li>
 );

2.3 JSX 中的列表渲染示例

列表渲染是 React 中常见的操作,通常使用 map() 方法来遍历数组并生成一组 JSX 元素。下面提供多个不同场景的列表渲染示例:

2.3.1 基础列表渲染
const fruits = ['Apple', 'Banana', 'Orange'];

function FruitList() {
  return (
    <ul>
      {fruits.map((fruit, index) => (
        <li key={index}>{fruit}</li>
      ))}
    </ul>
  );
}
2.3.2 对象数组渲染
const users = [
  { id: 1, name: 'Alice', age: 25 },
  { id: 2, name: 'Bob', age: 30 },
  { id: 3, name: 'Charlie', age: 35 }
];

function UserTable() {
  return (
    <table>
      <thead>
        <tr>
          <th>Name</th>
          <th>Age</th>
        </tr>
      </thead>
      <tbody>
        {users.map(user => (
          <tr key={user.id}>
            <td>{user.name}</td>
            <td>{user.age}</td>
          </tr>
        ))}
      </tbody>
    </table>
  );
}
2.3.3 带条件的列表渲染

在 JSX 中,我们经常需要根据条件来决定是否渲染某些内容。JavaScript 的逻辑与 (&&) 运算符和三元运算符 (?:) 是两种常用的条件渲染方式。

1. 逻辑与 (&&) 运算符
{condition && <Component />}

condition 为真时,渲染 <Component />;否则不渲染任何内容。

示例
function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn && <h1>Welcome back!</h1>}
    </div>
  );
}

// 使用
<Greeting isLoggedIn={true} />  // 显示 "Welcome back!"
<Greeting isLoggedIn={false} /> // 不显示任何内容
特点
  • 简洁,适合简单的条件渲染
  • 当条件为 false 时,React 会跳过渲染(不会渲染 nullfalse
  • 注意:如果 condition0 这样的 falsy 值,会渲染 0 而不是跳过
2. 三元运算符 (?:)
{condition ? <ComponentA /> : <ComponentB />}

condition 为真时,渲染 <ComponentA />;否则渲染 <ComponentB />

示例
function Greeting({ isLoggedIn }) {
  return (
    <div>
      {isLoggedIn ? (
        <h1>Welcome back!</h1>
      ) : (
        <h1>Please sign in.</h1>
      )}
    </div>
  );
}

// 使用
<Greeting isLoggedIn={true} />  // 显示 "Welcome back!"
<Greeting isLoggedIn={false} /> // 显示 "Please sign in."
特点
  • 适合需要两种不同渲染结果的场景
  • 更明确地表达两种可能性
  • 可以嵌套使用(但不推荐过于复杂的嵌套)
3. 在 JSX 外部使用 if 语句
提前决定要渲染的内容
function Greeting({ isLoggedIn }) {
  let content;
  
  if (isLoggedIn) {
    content = <h1>Welcome back!</h1>;
  } else {
    content = <h1>Please sign in.</h1>;
  }

  return <div>{content}</div>;
}
function renderContent(isLoggedIn) {
  if (isLoggedIn) {
    return <h1>Welcome back!</h1>;
  }
  return <h1>Please sign in.</h1>;
}

function Greeting({ isLoggedIn }) {
  return <div>{renderContent(isLoggedIn)}</div>;
}
4. 立即执行函数来包含 if 逻辑
function Greeting({ isLoggedIn }) {
  return (
    <div>
      {(() => {
        if (isLoggedIn) {
          return <h1>Welcome back!</h1>;
        } else {
          return <h1>Please sign in.</h1>;
        }
      })()}
    </div>
  );
}
5.使用 switch 语句
function StatusIndicator({ status }) {
  let indicator;
  
  switch (status) {
    case 'loading':
      indicator = <Spinner />;
      break;
    case 'success':
      indicator = <Checkmark />;
      break;
    case 'error':
      indicator = <ErrorIcon />;
      break;
    default:
      indicator = null;
  }

  return <div>{indicator}</div>;
}
6.比较
方法 适用场景 优点 缺点
外部 if 复杂条件逻辑 清晰易读 需要额外变量
IIFE 简单内联条件 保持内联 语法稍复杂
提取函数 可复用的条件渲染 可复用,易于测试 需要跳转查看逻辑
switch 多个互斥条件 结构清晰 代码量较多
&& 运算符 单一条件,渲染或不渲染 简洁 只能处理单一条件
三元运算符 二选一渲染 表达式形式,内联 嵌套时难以阅读
最佳实践建议
处理 false 值的注意事项
// 当 count 为 0 时,&& 会渲染 0
{count && <Message count={count} />}

// 修复方法:明确布尔值转换
{count > 0 && <Message count={count} />}
{!!count && <Message count={count} />}
  1. 简单条件:使用 && 更简洁
  2. 二选一渲染:使用三元运算符
  3. 避免复杂嵌套:如果条件太复杂,考虑提取为函数或组件
  4. 保持可读性:复杂的条件逻辑可以拆分成多个变量

选择哪种方式主要取决于具体场景和个人/团队的编码风格偏好,重要的是保持代码的一致性和可读性。

const products = [
  { id: 1, name: 'Laptop', inStock: true },
  { id: 2, name: 'Phone', inStock: false },
  { id: 3, name: 'Tablet', inStock: true }
];

function ProductList() {
  return (
    <ul>
      {products.map(product => (
        product.inStock && (
          <li key={product.id}>
            {product.name} - {product.inStock ? 'In Stock' : 'Out of Stock'}
          </li>
        )
      ))}
    </ul>
  );
}
2.3.4 嵌套列表渲染
const departments = [
  {
    id: 1,
    name: 'Development',
    employees: ['John', 'Jane', 'Mike']
  },
  {
    id: 2,
    name: 'Design',
    employees: ['Sarah', 'Tom']
  }
];

function DepartmentList() {
  return (
    <div>
      {departments.map(dept => (
        <div key={dept.id}>
          <h3>{dept.name}</h3>
          <ul>
            {dept.employees.map((employee, index) => (
              <li key={index}>{employee}</li>
            ))}
          </ul>
        </div>
      ))}
    </div>
  );
}
2.3.5 使用组件渲染列表项
function TodoItem({ todo }) {
  return (
    <li>
      <input type="checkbox" checked={todo.completed} />
      <span>{todo.text}</span>
    </li>
  );
}

function TodoList() {
  const todos = [
    { id: 1, text: 'Learn React', completed: true },
    { id: 2, text: 'Build a project', completed: false },
    { id: 3, text: 'Deploy to production', completed: false }
  ];

  return (
    <ul>
      {todos.map(todo => (
        <TodoItem key={todo.id} todo={todo} />
      ))}
    </ul>
  );
}
2.3.6 重要注意事项
  1. key 属性:列表中的每个元素都应该有一个唯一的 key 属性,通常使用数据中的 ID,

    // 好
    {items.map(item => <li key={item.id}>{item.name}</li>)}
    
    // 如果没有 ID,可以使用索引(但不推荐用于动态列表)
    {items.map((item, index) => <li key={index}>{item.name}</li>)}
    
  2. 避免在渲染时修改数据:不要在 map 内部修改原始数组

  3. 性能优化:对于大型列表,考虑使用虚拟滚动技术(如 react-window 库)

2.4 注意事项

  • JSX 中的表达式必须是返回值的表达式,不能使用语句(如 if 语句)
  • 对于条件渲染,可以使用三元运算符或逻辑与 (&&) 运算符
  • 对象不能直接作为子元素渲染,需要转换为字符串或提取特定属性
// 错误示例
const user = { name: 'John' };
const element = <div>{user}</div>; // 错误

// 正确示例
const element = <div>{user.name}</div>; // 正确
const element = <div>{JSON.stringify(user)}</div>; // 正确

JSX 中的 JavaScript 表达式使得动态 UI 的创建变得简单而强大。

image-20250701154101729

三、事件绑定+组件

3.1 React事件绑定

在 JSX 中绑定 React 事件与在 HTML 中绑定 DOM 事件类似,但有一些关键区别。以下是 React 事件绑定的主要特点和使用方法:

3.1.1 基本语法
<button onClick={handleClick}>
  点击我
</button>
(1)与 HTML 事件的区别
  1. 命名采用驼峰式onclickonClickonchangeonChange
  2. 传入函数而不是字符串onclick="handleClick()"onClick={handleClick}
  3. 默认行为阻止方式不同:不能通过返回 false 来阻止默认行为
(2)常见事件类型
  • onClick - 点击事件
  • onChange - 表单元素变化
  • onSubmit - 表单提交
  • onMouseEnter - 鼠标进入
  • onMouseLeave - 鼠标离开
  • onKeyDown - 键盘按下
  • onFocus - 获取焦点
  • onBlur - 失去焦点
3.1.2【1】事件处理函数
function handleClick(event) {
  // event 是合成事件(SyntheticEvent)
  console.log('按钮被点击了', event);
}

function App() {
  return <button onClick={handleClick}>点击</button>;
}

3.1.2 【2】 传递参数
(1)如果需要传递额外参数给事件处理函数
function handleClick(id, event) {
  console.log('ID:', id, '事件:', event);
}

function App() {
  return (
    <button onClick={(e) => handleClick(123, e)}>
      带参数点击
    </button>
  );
}

image-20250701175635340

🔍 特点分析:
  • 事件对象正确传递onClick={(e) => handleClick(123, e)} 中的 e 是 React 事件对象,被显式传递给了 handleClick 函数。
  • 函数签名匹配handleClick(id, event) 的两个参数(idevent)都得到了正确的值。
  • 用途场景:当需要访问事件对象(如阻止默认行为、获取事件目标等)时,这种方式是推荐的。
(2)箭头函数的

不能直接写函数调用,这事件绑定需要一个函数引用

function handleClick(event) {
    console.log('事件:', event);
}

const App = () => {

    return (
        <button onClick={() => handleClick(123)}>
            带参数点击
        </button>
    );
};

image-20250701175624204

🔍 问题分析:
  • 事件对象未传递onClick={() => handleClick(123)} 中的 handleClick 仅接收 123 作为参数,事件对象(event)没有被传入
  • 函数签名不匹配handleClick(event) 期望的是一个 event 参数,但实际传入的是 123,因此 event 参数会是 undefined
  • 潜在错误:如果 handleClick 中依赖 event 的值(如 event.target.valueevent.preventDefault() 等),会导致运行时错误或行为异常。
3.1.3 类组件中的绑定

在类组件中,通常需要在构造函数中绑定 this 或使用箭头函数:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    console.log('this:', this);
  }

  // 或者使用箭头函数自动绑定 this
  handleClick = () => {
    console.log('this:', this);
  }

  render() {
    return <button onClick={this.handleClick}>点击</button>;
  }
}
3.1.4阻止默认行为和事件冒泡
function handleClick(event) {
  event.preventDefault(); // 阻止默认行为
  event.stopPropagation(); // 阻止事件冒泡
  console.log('事件处理');
}
3.1.5 合成事件(SyntheticEvent)

React 的事件是合成事件,是对原生事件的跨浏览器包装,具有与原生事件相同的接口,包括:

  • event.preventDefault()
  • event.stopPropagation()
  • event.target
  • event.currentTarget
3.1.6 注意事项
  1. 事件处理函数中的 this 默认不是组件实例,需要手动绑定
  2. 避免在 render 方法中创建新函数,可能导致不必要的重新渲染
  3. React 17+ 中事件委托不再附加到 document,而是附加到 React 渲染的根 DOM 容器

正确的事件绑定方式可以帮助你编写更高效、更易维护的 React 组件。

3.2 React组件

3.2.1 基础使用

React 组件是构建用户界面的独立、可复用的代码单元,将 UI 拆分为独立模块。每个组件管理自身的状态和逻辑,通过组合形成复杂界面。

image-20250703132838717

/*原生写法*/
function Button (){
	return <button>Button</button>
}

/*或者使用箭头函数的写法*/
const Button = () =>{
	return <button>Button</button>
}

使用上

/*自闭和*/
<Button/>
/*成对标签*/
<Button/><Button/>
3.2.2 基础样式控制

在 React 中,控制组件样式有多种基础方式,每种方式各有适用场景。以下是主要方法及代码示例:

1. 内联样式 (Inline Styles)

直接在 JSX 元素中使用 style 属性,传入样式对象(属性名需驼峰命名

function Button() {
  return (
    <button 
      style={{
        padding: "10px 20px",
        backgroundColor: "blue",
        color: "white",
        borderRadius: "4px",
        cursor: "pointer"
      }}
    >
      点击我
    </button>
  );
}

特点

  • ✅ 简单直接,适合动态样式
  • ❌ 无法使用伪类(:hover)和媒体查询
  • ❌ 性能不如 CSS 类(大量使用可能影响渲染)

2. 外部 CSS 文件

传统 CSS 引入方式(最常用)

/* Button.css */
.my-button {
  padding: 10px 20px;
  background-color: blue;
  color: white;
  border-radius: 4px;
  cursor: pointer;
}

.my-button:hover {
  background-color: darkblue; /* 支持伪类 */
}

组件中引入:

import "./Button.css";

function Button() {
  return <button className="my-button">点击我</button>;
}

特点

  • ✅ 支持所有 CSS 特性
  • ✅ 浏览器缓存优化
  • ❌ 全局作用域(需注意类名冲突)

3. CSS Modules(推荐)

自动局部作用域(文件命名:[name].module.css

/* Button.module.css */
.button {
  padding: 10px 20px;
  background: blue;
}

组件中使用:

import styles from "./Button.module.css";

function Button() {
  return <button className={styles.button}>点击我</button>;
}

编译后类名会变成唯一值(如 Button_button__1H2k5

特点

  • ✅ 避免类名冲突
  • ✅ 支持所有 CSS 特性
  • ✅ 与普通 CSS 写法一致

4. CSS-in-JS 库 (如 styled-components)

通过 JavaScript 编写样式(需安装库)

npm install styled-components
import styled from "styled-components";

// 创建带样式的组件
const StyledButton = styled.button`
  padding: 10px 20px;
  background: ${props => props.primary ? "blue" : "gray"};
  color: white;
  border-radius: 4px;
  
  &:hover {
    background: darkblue; /* 支持伪类 */
  }
`;

function App() {
  return (
    <>
      <StyledButton>普通按钮</StyledButton>
      <StyledButton primary>主要按钮</StyledButton>
    </>
  );
}

特点

  • ✅ 动态样式支持最佳
  • ✅ 自动处理作用域
  • ❌ 增加包体积
  • ❌ 学习曲线较陡

5. 实用类库 (Tailwind CSS)

原子化 CSS 方案(需配置)

function Button() {
  return (
    <button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
      点击我
    </button>
  );
}

特点

  • ✅ 快速原型开发
  • ✅ 响应式设计便捷
  • ❌ 需学习类名系统
  • ❌ HTML 可能臃肿

选择建议:
场景 推荐方式
简单原型/少量动态样式 内联样式
传统项目/团队熟悉 CSS 外部 CSS + BEM
组件化/避免类名冲突 CSS Modules
复杂动态样式/主题系统 CSS-in-JS
快速开发/统一设计系统 Tailwind CSS

最佳实践提示

  1. 大型项目推荐 CSS ModulesCSS-in-JS
  2. 避免过度使用内联样式(性能考虑)
  3. 动态样式可结合:className 条件拼接 或 CSS 变量
  4. 使用 :where() 降低 CSS 特异性(如 :where(.card) .title

四、useState

useState 是 React 中用于声明组件状态的一个 Hook,它让函数组件能够拥有自己的内部状态。(向组件添加一个状态变量,一旦变化UI也随之变化**(数据驱动视图**))

通过 useState,你可以在组件内部保存数据并在数据发生变化时重新渲染组件。

image-20250703133721750

4.1 useState 基础结构

const [state, setState] = useState(initialValue);
  • state:当前状态值(只读)
  • setState:更新状态的函数
  • initialValue:状态的初始值(可以是任何类型)
React原则
🔒 状态是只读的
📖 状态读取
🚫 不可直接修改
🔄 状态更新
🆕 必须创建新值替换
✨ 状态替换
📥 状态初始化

4.2 状态不可变性原则详解

4.2.1 为什么状态必须不可变?
  1. 变更检测:React 通过引用比较检测状态变化
  2. 性能优化:避免不必要的重新渲染
  3. 可预测性:状态变更路径清晰可追踪
  4. 时间旅行调试:支持状态历史记录和回滚
4.2.2 错误 vs 正确更新方式对比

必须创建状态的新副本进行替换,而不是直接修改现有状态。

状态类型 错误方式(直接修改) 正确方式(创建新值)
对象 user.age = 31;
setUser(user);
setUser({...user, age: 31});
数组 items.push('new');
setItems(items);
setItems([...items, 'new']);
嵌套对象 user.address.city = 'Paris'; setUser({...user, address: {...user.address, city: 'Paris'}});

image-20250703145808001

4.3 待处理:状态管理实战模式

1. 表单状态处理
function UserForm() {
  const [user, setUser] = useState({
    name: '',
    email: '',
    preferences: { theme: 'light', notifications: true }
  });

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

  const toggleTheme = () => {
    setUser(prev => ({
      ...prev,
      preferences: {
        ...prev.preferences,
        theme: prev.preferences.theme === 'light' ? 'dark' : 'light'
      }
    }));
  };

  return (
    <form>
      <input value={user.name} onChange={e => handleChange('name', e.target.value)} />
      <input value={user.email} onChange={e => handleChange('email', e.target.value)} />
      <button type="button" onClick={toggleTheme}>
        切换主题(当前:{user.preferences.theme}</button>
    </form>
  );
}

image-20250703144751042

2. 列表状态管理
function TodoList() {
  const [todos, setTodos] = useState([
    { id: 1, text: '学习React', completed: false }
  ]);
  
  // 添加新任务(创建新数组)
  const addTodo = text => {
    setTodos(prev => [
      ...prev,
      { id: Date.now(), text, completed: false }
    ]);
  };
  
  // 切换任务状态(创建新对象)
  const toggleTodo = id => {
    setTodos(prev => 
      prev.map(todo => 
        todo.id === id 
          ? { ...todo, completed: !todo.completed } 
          : todo
      )
    );
  };
  
  // 删除任务(创建过滤后的新数组)
  const deleteTodo = id => {
    setTodos(prev => prev.filter(todo => todo.id !== id));
  };
  
  return (
    <div>
      <ul>
        {todos.map(todo => (
          <li key={todo.id}>
            <input 
              type="checkbox"
              checked={todo.completed}
              onChange={() => toggleTodo(todo.id)}
            />
            <span style={{ textDecoration: todo.completed ? 'line-through' : 'none' }}>
              {todo.text}
            </span>
            <button onClick={() => deleteTodo(todo.id)}>删除</button>
          </li>
        ))}
      </ul>
    </div>
  );
}

4.4 待处理:高级技巧与优化策略

1. 函数式更新
// 当新状态依赖旧状态时使用
setCount(prevCount => prevCount + 1);

// 批量更新示例
const incrementTwice = () => {
  setCount(prev => prev + 1); // 使用前值计算
  setCount(prev => prev + 1); // 确保两次更新都生效
};
2. 惰性初始化
// 复杂初始状态的计算只执行一次
const [data, setData] = useState(() => {
  const expensiveValue = calculateExpensiveValue();
  return expensiveValue;
});
3. 状态结构优化
复杂状态
拆分为多个useState
使用useReducer
使用Immer库
提高可读性
集中状态逻辑
简化不可变更新
4. 使用 Immer 简化不可变更新
import produce from 'immer';

const [user, setUser] = useState({
  name: 'Alice',
  profile: {
    level: 5,
    achievements: ['新手', '探索者']
  }
});

// 使用 Immer 进行"可变式"更新(底层仍不可变)
const addAchievement = title => {
  setUser(produce(draft => {
    draft.profile.achievements.push(title);
  }));
};

const updateLevel = () => {
  setUser(produce(draft => {
    draft.profile.level += 1;
  }));
};

网站公告

今日签到

点亮在社区的每一天
去签到