LESSON 4.5 / MODERN FRONTEND

数据、状态与 URL 驱动界面

状态是会影响界面并随交互变化的数据。把来源分清后,服务端数据、URL 条件、表单输入和派生结果才不会互相打架。

预计阅读约 25 到 40 分钟
完成结果完成可分享的任务筛选,并能解释每一项状态由谁拥有、怎样改变、如何触发界面更新。
本课目标
  • 完成可分享的任务筛选,并能解释每一项状态由谁拥有、怎样改变、如何触发界面更新。
  • 内容数据与界面结构分离
  • 实现数据、状态与 URL 驱动界面后,应当完成可分享的任务筛选,并能解释每一项状态由谁拥有、怎样改变、如何触发界面更新,并保留类型检查、生产构建、浏览器行为和请求结果作为可重复检查的依据。
开始之前
  • 完成 4.4「React:用组件组织界面」
01

FOUNDATION

必须理解

状态(State)是会随交互变化并影响界面的数据。React 根据当前状态重新计算界面;如果同一事实被重复保存,多个副本就可能互相矛盾。

本课依次讲清内容数据与界面结构分离、状态、事件与重新渲染和URL 作为可分享、可前进后退的页面状态,最后通过“测试刷新、浏览器前进后退、空结果和重复点击”检查学习结果。

先建立整体直觉

餐厅叫号屏不需要另外保存“还有几桌”等每个结果,只要有排队名单就能计算。保存原始事实,其他显示值尽量现算。

贯穿本课的实际场景

任务数组是核心状态;当前筛选条件可以进入 URL;“已完成数量”由任务数组计算,不再单独维护一个容易失真的数字。

概念 1

内容数据与界面结构分离

先用白话理解

任务标题和完成状态属于内容数据;当前选中了哪个筛选、输入框写到哪里,则更像界面状态。先弄清一个值该由服务器、URL、页面还是小组件负责。

基础概念

内容数据描述产品中的事实,界面结构描述这些事实怎样呈现。把二者分离后,同一份任务数组可以生成列表、统计和筛选结果,而无需复制三份标记。

进一步理解

数据通常拥有稳定字段,例如 id、title、done;组件通过映射和条件渲染把它变成元素。改变数据会改变画面,改变布局不应反过来偷偷改业务事实。

每条可变记录需要稳定标识,React 才能在列表更新时追踪对应项。数组索引在排序或插入后会改变,不适合作为长期身份。

放进实际场景

任务数组作为唯一事实来源,列表逐条渲染,完成数量通过过滤计算,空数组则显示空状态。

容易混淆的地方

把 JSX 写进数据字段会把内容和表现绑死;把完成数量再次保存又会产生与任务数组不一致的第二份事实。

这一小节记住:保存最小事实,让界面和统计从事实计算出来。

概念 2

状态、事件与重新渲染

先用白话理解

点击事件会触发状态更新,React 再根据新状态画一次界面。别直接在原数组上改值,创建一份新数组更容易让 React 和你自己都看清变化。

基础概念

状态是组件需要在多次渲染之间记住、且变化后会影响界面的数据。事件触发状态更新,React 根据新状态再次计算组件树并更新必要的 DOM。

进一步理解

状态更新不是直接改当前变量,而是请求 React 安排下一次渲染。根据旧值计算新值时使用函数式更新,可以避免读取过期快照。

对象和数组应创建新值而不是原地修改,使变化可被可靠比较。共享状态放到最近共同父组件,避免多个组件各自维护不同版本。

放进实际场景

点击任务按钮后,setTasks 根据 id 生成新数组,只替换目标任务;React 重新渲染并更新对应文字。

容易混淆的地方

普通局部变量会在每次渲染重新计算,改变它也不会通知 React;状态则由 React 保存并触发新渲染。

这一小节记住:事件提出变化,状态保存事实,渲染把最新事实呈现出来。

概念 3

URL 作为可分享、可前进后退的页面状态

先用白话理解

像 ?filter=done 这样的 URL 可以把筛选状态保留下来,刷新、分享和前进后退都更自然。还没提交的输入文字通常只放组件里就够了。

基础概念

URL 可以保存适合分享、刷新和历史导航的页面状态,例如搜索词、筛选条件和页码。它是浏览器与用户都能看见的状态容器。

进一步理解

当页面结果由 URL 决定,复制地址即可重现视图,前进后退也能恢复选择。敏感信息、巨大对象和未提交输入不适合放进 URL。

组件本地状态适合短暂交互,服务器数据适合长期共享事实,URL 状态适合导航语义。先判断生命周期与共享范围,再选择位置。

放进实际场景

`/tasks?filter=done` 表示只看已完成任务;刷新或发给同伴后仍得到相同筛选,而输入框正在编辑的草稿留在本地状态。

容易混淆的地方

URL 参数不等于数据库数据,它可以描述当前视图,却不能替代服务端保存任务记录和权限判断。

这一小节记住:需要可分享、可刷新、可前进后退的视图选择,优先考虑 URL。

保存事实,计算结果
运行前先确认
  • 组件已从 React 导入 useState,并存在 Task 类型。
  • tasks 是当前页面中任务事实的唯一所有者。
const [tasks, setTasks] = useState<Task[]>([]);
const completedCount = tasks.filter((task) => task.done).length;

function toggleTask(id: string) {
  setTasks((current) => current.map((task) =>
    task.id === id ? { ...task, done: !task.done } : task
  ));
}
这段代码在做什么

阅读数据、状态与 URL 驱动界面示例时,完成数量由任务数组计算,切换时创建新数组,避免两个状态源互相不同步。

  1. useState 保存原始任务数组,completedCount 每次渲染都从数组计算。
  2. toggleTask 使用函数式更新取得最新数组,避免闭包中的旧值。
  3. map 创建新数组,仅为匹配 id 的任务创建新对象并反转 done。
你应该观察到

点击某条任务后只有该项状态变化,完成数量同步更新;原数组没有被直接修改,也不存在需要额外同步的 completedCount 状态。

02

AI COLLABORATION

AI 如何参与

在数据、状态与 URL 驱动界面这一课,AI 负责根据真实材料解释内容数据与界面结构分离并指出遗漏,学习者负责控制范围、执行修改和核对类型检查、生产构建、浏览器行为和请求结果。

推荐协作顺序

  1. 1

    把页面中的所有可变值列给 AI,让它先分类来源。

  2. 2

    让它指出哪些值可以从其它数据计算,不要急着写 useState。

  3. 3

    实现后沿一次点击追踪状态变化,确认只有一个可信来源。

可直接使用的 Prompt
请把这页数据分成服务端数据、URL 状态、组件状态、表单输入和可计算结果。逐项说明唯一来源与更新事件,再给最小实现顺序。不要重复保存可以计算的值。
应该得到什么

每个值只有一个可信来源,completedCount 从 tasks 计算,filter 可由 URL 表达;数据、状态与 URL 驱动界面的人工验收必须回到真实页面、请求、终端、测试或数据结果,不能用 AI 的文字说明代替。

人工检查清单

  • 确认 AI 没把可计算值复制进 state,也没有用 effect 在多个状态之间来回同步。
  • 要求 AI 为每个值标出唯一所有者,并解释为何属于服务器、URL、页面或组件状态。
  • 通过刷新、分享链接、前进后退和快速连续点击验证状态没有重复或丢失。
03

COMMON TRAPS

常见误区

下面三类问题会让任务看似完成,却经不起刷新、错误输入或真实环境检查。先看现象,再找原因和修正方法。

误区 1

重复保存派生状态

你会看到
总数和列表不同步
为什么发生
同一事实有两份可写副本,只要一次更新漏掉其中一份,两者就会分叉。
怎样纠正
从唯一数据源即时计算
误区 2

多个组件各存一份真相

你会看到
一次操作只更新其中一处
为什么发生
每个副本都有自己的更新时机,组件之间无法自动保持一致。
怎样纠正
把状态提升到共同拥有者
误区 3

用副作用修补数据流

你会看到
更新顺序难以预测并出现循环
为什么发生
副作用在渲染后运行,若它又修改依赖项,更新顺序会变得隐蔽甚至循环。
怎样纠正
先检查是否可以在渲染或事件中直接完成
04

HANDS-ON

动手任务

完成可分享的任务筛选,并能解释每一项状态由谁拥有、怎样改变、如何触发界面更新。

准备条件
  • 完成 4.4「React:用组件组织界面」

跟着做

  1. 01

    用 useState 保存本地任务数组和输入值。

  2. 02

    通过不可变更新实现新增与切换完成。

  3. 03

    用 filter 从 URL 或选择项决定显示列表。

  4. 04

    使用 filter 和 reduce 计算可见任务与完成数量。

  5. 05

    测试刷新、浏览器前进后退、空结果和重复点击。

完成标志

完成可分享的任务筛选,并能解释每一项状态由谁拥有、怎样改变、如何触发界面更新。

加餐挑战

把筛选条件写入 URL 查询参数,并分享链接验证另一窗口打开相同筛选。

离开本课前,自问四件事

  • 内容数据与界面结构应如何分离?
  • 为什么 React 状态更新后会重新渲染,而普通变量不会?
  • 哪些信息适合放进 URL,哪些不适合?
  • 完成任务数量为什么应从任务数组计算而不是单独保存?
完成本课了吗?

确认完成后,会同步更新学习中心的课程学习进度。