掌握 React 的动态侧面:状态、事件和条件渲染!(React 第3天)
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 更新 query → filtered 重新计算 → JSX 重新渲染匹配的列表。
React 重新渲染组件的工作原理
当 state 或 props 发生变化时,React 会:
- 重新运行 组件函数。
- 创建 一个新的虚拟 DOM 树。
- 对比 新旧两棵树的差异(diff)。
- 仅将 必要的更改 应用 到真实的 DOM 上。
类比
可以把它想象成重新绘制草图:只擦除并重新绘制那些发生变化的部分。
渲染周期可视化
该图展示了从 state/prop 变化 → render → commit → 浏览器绘制 的流程。
关键要点
- 不可变性 – 始终将 state 视为不可变的;React 依赖引用变化来检测更新。
- 批处理 – 同一事件循环内的多个 state 更新会被批处理为一次渲染,以提升性能。
- 记忆化 – 使用
React.memo、useMemo和useCallback来避免不必要的重新渲染。
调试技巧
在 React DevTools 中启用 “Highlight updates” 选项,以查看状态变化后哪些组件重新渲染。结合 console.log 语句可以追踪渲染流程。
🎉 您已准备就绪!
您现在拥有了让您的 React 组件 stateful、interactive 和 dynamic 的核心工具。尝试这些示例,敢于弄坏它们,然后再修复——学习就在这个过程中进行。祝编码愉快!
状态与生命周期概览

调试技巧: 使用 React DevTools Profiler 记录渲染并发现不必要的渲染。
状态不可变性 – 为什么不能直接修改
状态应被视为 只读。直接变更它(例如 array.push(item))不会通知 React,导致 UI 不会更新。
- 为什么? React 通过检查 引用相等性 来检测变化。变更后引用仍然相同,React 会跳过重新渲染。
- 最佳实践: 在更新状态时创建一个新的对象/数组。
// ❌ 直接修改原数组
array.push(item);
setArray(array);
// ✅ 创建一个新数组
setArray([...array, item]);

常见错误: 修改嵌套对象。请使用深拷贝或像 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: 用于配置数据、来自父组件的值,或任何在子组件内部应保持不可变的内容。
- 何时使用 State: 用于因用户交互、网络响应、计时器等随时间变化的数据。
- 常见错误: 将 派生数据 存入 state。应当在渲染时根据 props/state 动态计算,以避免同步错误。
调试技巧: 如果 UI 没有更新,在 render 函数内部同时打印 props 和 state,检查它们是否包含预期的值。
完成总结 – 让你的 React 应用活起来
您现在已经掌握了:
- State – 不可变更新、批处理和调试。
- Props – 父组件到子组件的单向数据流。
- Lifecycle basics – 组件何时挂载、更新和卸载。
这些概念驱动了计数器、表单、仪表盘和聊天界面等真实世界的功能。
下一步: 第 4 天 – 列表、键和副作用。
练习想法: 构建一个动态 todo 列表,让用户可以添加、编辑和删除项目。请记住:
- 永远不要直接修改 原始列表;始终创建一个新数组。
- 通过使用函数式更新 来利用批处理,当多个状态更改依赖于先前的状态时。
- 正确使用键,以保持列表项的稳定性。
祝编码愉快,继续 React! 🚀
