agentmentoragentmentor

第 07 节:受控表单:组合一个小任务组件

本节 objectives:

  • 能解释受控输入框为什么由 state 驱动。
  • 能用 valueonChange 处理文本输入。
  • 能组合 props、state、事件、条件和列表做一个小组件。

先修:前六节全部概念 | 上一节 << 06 | 下一节:无

输入框看起来自己会变,React 里要有人负责它

浏览器输入框本来能自己保存文字。但在 React 组件里,如果你希望输入值参与校验、提交、清空或渲染其他 UI,就要让 React state 成为它的单一来源。React DOM 的 input 文档把带 value 并通过 onChange 同步 state 的输入称为受控输入。1

这节把前面所有零件合成一个小任务组件。

讲解

受控输入的核心形状:

jsx
const [text, setText] = useState("");
return (  <input    value={text}    onChange={(event) => setText(event.target.value)}  />);

value={text} 表示输入框显示 state。onChange 表示用户输入时,把新值写回 state。少了前者,React 不控制显示值;少了后者,输入框会被锁住。

提交表单时通常要阻止浏览器默认刷新页面:

jsx
function handleSubmit(event) {  event.preventDefault();}

现在把受控输入、列表渲染、条件渲染放在一起:输入任务标题,提交后加入列表,空输入不提交。

跟我做一遍(worked example)

jsx
import { useState } from "react";
function MiniTaskApp() {  const [text, setText] = useState("");  const [tasks, setTasks] = useState([]);
  function handleSubmit(event) {    event.preventDefault();    const title = text.trim();    if (!title) return;
    const nextTask = {      id: crypto.randomUUID(),      title,      done: false,    };
    setTasks([...tasks, nextTask]);    setText("");  }
  return (    <section>      <form onSubmit={handleSubmit}>        <label>          新任务          <input            value={text}            onChange={(event) => setText(event.target.value)}          />        </label>        <button type="submit">添加</button>      </form>
      {tasks.length === 0 ? (        <p>还没有任务。</p>      ) : (        <ul>          {tasks.map((task) => (            <li key={task.id}>{task.title}</li>          ))}        </ul>      )}    </section>  );}

为什么这里不用 effect:提交任务、更新输入框、渲染列表都发生在 React 内部。没有外部系统要同步。

换你补全(faded example)

给任务增加"完成/未完成"切换。空白处是不可变更新数组的关键判断。

jsx
function toggleTask(id) {  setTasks(    tasks.map((task) =>      task.id === id        ? { ...task, done: ________ }        : ________    )  );}
// li 内:<button type="button" onClick={() => ________(task.id)}>  {task.done ? "标为未完成" : "标为完成"}</button>

参考答案:

jsx
function toggleTask(id) {  setTasks(    tasks.map((task) =>      task.id === id        ? { ...task, done: !task.done }        : task    )  );}
<button type="button" onClick={() => toggleTask(task.id)}>

常见错误是直接写 task.done = !task.done。初学阶段先坚持创建新对象和新数组,这样 React 的更新路径更清楚。

小结 + 通向下一节

受控表单把输入值交给 React state 管。到这里,你已经能把组件、JSX、props、state、事件、条件、列表和基础 effect 放回同一张图里。

下一步不是背更多 API,而是拿一个真实小界面反复拆:哪些是 props,哪些是 state,哪些只是从现有数据渲染出来,哪些才需要 effect。

Footnotes

  1. <input> React DOM Component — https://react.dev/reference/react-dom/components/input

练习

Level 1: 在你的 React 项目里复现 MiniTaskApp,能添加任务并清空输入框。

提示 1

表单先只管输入和提交。

提示 2

列表先只管显示。

提示 3

切换状态最后加,别一开始全塞进去。

自评