掌握 React 的动态侧面:状态、事件和条件渲染!(React 第3天)

发布: (2026年1月11日 GMT+8 12:30)
11 min read
原文: Dev.to

Source: Dev.to

Source:

状态管理:引入 useState 用于本地数据

State 是 React 用来记住随时间变化的数据的方式——比如计数器的值或表单输入。与从父组件传递下来的 props 不同,state 对组件是 私有 的,并且在更新时会触发重新渲染。

useState 是一个 Hook(后面会详细介绍),它会为你提供一个状态变量和一个设置函数。需要从 React 中导入:

import { useState } from 'react';

工作原理

const [value, setValue] = useState(initialValue);
  • value 为只读。
  • 调用 setValue 来更新它。

实时示例:简单计数器

import { useState } from 'react';

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

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}

点击按钮 → 状态更新 → 组件重新渲染并显示新的计数。

实际案例:登录表单跟踪输入

import { useState } from 'react';

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

  return (
    <input
      type="text"
      value={username}
      onChange={e => setUsername(e.target.value)}
      placeholder="Username"
    />
  );
}

State 让输入保持同步,随时可以提交。

常见错误

  • 直接变更count++ 不会触发重新渲染。必须使用 setter。
  • 跳过 setter – 在没有 setValue 的情况下更新变量不会产生任何效果。

调试技巧

console.log('Current count:', count);

将日志放在组件内部,以便在每次渲染时查看当前值。记住,重新渲染是异步的。

Source:

事件处理:响应用户操作

React 中的事件看起来像 HTML,但使用 camelCase(例如 onClick)并接收函数引用。

类比: 给门铃装上监听器——当用户“按铃”(点击)时,你的代码会响应。

实时示例(来自 Counter 示例)

<button onClick={() => setCount(count + 1)}>Increment</button>

表单事件

  • onChange – 捕获输入变化。
  • onSubmit – 处理表单提交。

实际案例:Todo 应用中添加条目

import { useState } from 'react';

function TodoForm({ addTodo }) {
  const [text, setText] = useState('');

  const handleSubmit = e => {
    e.preventDefault();          // 防止页面刷新
    addTodo(text);
    setText('');                 // 清空输入框
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={text}
        onChange={e => setText(e.target.value)}
        placeholder="New todo"
      />
      <button type="submit">Add</button>
    </form>
  );
}

常见错误

  • 在表单上忘记调用 e.preventDefault() → 页面会重新加载。
  • 在每次渲染时定义大量内联函数 → 考虑使用 useCallback 或将函数抽离。

调试技巧

打开 React DevTools,检查组件的 props 和 state,并打印事件对象:

const handleClick = e => {
  console.log(e);
  // …你的逻辑
};

条件渲染:基于显示/隐藏的逻辑

根据条件渲染不同的 UI——就像普通 JavaScript 中的 if/else 一样。

方法

技术语法
三元运算符{condition ? <A /> : <B />}
短路{condition && <A />}
提前返回if (!condition) return null;

实时示例:切换可见性

import { useState } from 'react';

function Toggle() {
  const [isVisible, setIsVisible] = useState(false);

  return (
    <div>
      <button onClick={() => setIsVisible(!isVisible)}>Toggle</button>
      {isVisible && <p>Now you see me!</p>}
    </div>
  );
}

实际案例:加载指示器

import { useState, useEffect } from 'react';

function DataFetcher() {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch('/api/data')
      .then(res => res.json())
      .then(json => {
        setData(json);
        setLoading(false);
      });
  }, []);

  return loading ? (
    <p>Loading...</p>
  ) : (
    <pre>{JSON.stringify(data)}</pre>
  );
}

常见错误

  • if 语句放在 JSX 返回 之外——JSX 是表达式,应该在内部使用三元运算符或短路。
  • 深度嵌套的条件语句 → 将其提取为独立组件以提升可读性。

动态 UI 更新:将所有内容整合在一起

将状态、事件和条件结合起来,实现能够响应用户操作的 UI——例如,输入时即可更新的可过滤列表。

实际案例:搜索表单

import { useState } from 'react';

function SearchList() {
  const [query, setQuery] = useState('');
  const items = ['Apple', 'Banana', 'Cherry'];

  const filtered = items.filter(item =>
    item.toLowerCase().includes(query.toLowerCase())
  );

  return (
    <div>
      <input
        type="text"
        value={query}
        onChange={e => setQuery(e.target.value)}
        placeholder="Search fruits"
      />
      <ul>
        {filtered.length ? (
          filtered.map(item => <li key={item}>- {item}</li>)
        ) : (
          <li>No results</li>
        )}
      </ul>
    </div>
  );
}

输入onChange 更新 queryfiltered 重新计算 → JSX 重新渲染匹配的列表。

React 重新渲染组件的工作原理

stateprops 发生变化时,React 会:

  1. 重新运行 组件函数。
  2. 创建 一个新的虚拟 DOM 树。
  3. 对比 新旧两棵树的差异(diff)。
  4. 仅将 必要的更改 应用 到真实的 DOM 上。

类比

可以把它想象成重新绘制草图:只擦除并重新绘制那些发生变化的部分。

渲染周期可视化

React render cycle diagram

该图展示了从 state/prop 变化 → render → commit → 浏览器绘制 的流程。

关键要点

  • 不可变性 – 始终将 state 视为不可变的;React 依赖引用变化来检测更新。
  • 批处理 – 同一事件循环内的多个 state 更新会被批处理为一次渲染,以提升性能。
  • 记忆化 – 使用 React.memouseMemouseCallback 来避免不必要的重新渲染。

调试技巧

在 React DevTools 中启用 “Highlight updates” 选项,以查看状态变化后哪些组件重新渲染。结合 console.log 语句可以追踪渲染流程。

🎉 您已准备就绪!

您现在拥有了让您的 React 组件 statefulinteractivedynamic 的核心工具。尝试这些示例,敢于弄坏它们,然后再修复——学习就在这个过程中进行。祝编码愉快!

状态与生命周期概览

React 中的状态和生命周期 – 初学者指南

调试技巧: 使用 React DevTools Profiler 记录渲染并发现不必要的渲染。

状态不可变性 – 为什么不能直接修改

状态应被视为 只读。直接变更它(例如 array.push(item)不会通知 React,导致 UI 不会更新。

  • 为什么? React 通过检查 引用相等性 来检测变化。变更后引用仍然相同,React 会跳过重新渲染。
  • 最佳实践: 在更新状态时创建一个新的对象/数组。
// ❌ 直接修改原数组
array.push(item);
setArray(array);

// ✅ 创建一个新数组
setArray([...array, item]);

React 状态错误:不会抛出错误但会破坏 UI

常见错误: 修改嵌套对象。请使用深拷贝或像 Immer 这样的库。

批处理 – 为提高效率而分组更新

React 会自动 批处理 多个状态更新为一次重新渲染,尤其在事件处理函数或生命周期方法内部。

// Both updates are batched → count increases by 2 in one render
setCount(c => c + 1);
setCount(c => c + 1);
  • 为什么? 减少 DOM 抖动并提升性能。
  • 陷阱: 在异步代码中,更新 不会 立即生效。需要在状态改变后执行操作时,请使用回调或 useEffect

Props 与 State – 关键比较

Aspect(方面)Props(属性)State(状态)
Source(来源)从父组件 向下 传递由组件 内部 管理
Mutability(可变性)只读 – 子组件不能更改通过 setter 函数(setX可变
Trigger(触发)更改 prop 会导致子组件重新渲染更改 state 会重新渲染组件 以及 其子组件
Analogy(类比)父母的礼物 – 不能随意更改你自己的钱包 – 可以消费或添加

状态和 Props 是什么?

  • 何时使用 Props: 用于配置数据、来自父组件的值,或任何在子组件内部应保持不可变的内容。
  • 何时使用 State: 用于因用户交互、网络响应、计时器等随时间变化的数据。
  • 常见错误:派生数据 存入 state。应当在渲染时根据 props/state 动态计算,以避免同步错误。

调试技巧: 如果 UI 没有更新,在 render 函数内部同时打印 props 和 state,检查它们是否包含预期的值。

完成总结 – 让你的 React 应用活起来

您现在已经掌握了:

  • State – 不可变更新、批处理和调试。
  • Props – 父组件到子组件的单向数据流。
  • Lifecycle basics – 组件何时挂载、更新和卸载。

这些概念驱动了计数器、表单、仪表盘和聊天界面等真实世界的功能。

下一步: 第 4 天 – 列表、键和副作用

练习想法: 构建一个动态 todo 列表,让用户可以添加、编辑和删除项目。请记住:

  1. 永远不要直接修改 原始列表;始终创建一个新数组。
  2. 通过使用函数式更新 来利用批处理,当多个状态更改依赖于先前的状态时。
  3. 正确使用键,以保持列表项的稳定性。

祝编码愉快,继续 React! 🚀

Back to Blog

相关文章

阅读更多 »