系列回顾:
在前面的文章中,我们已经掌握了State、Props、事件处理、列表渲染和条件渲染。我们的应用已经能展示动态内容并响应用户的点击。现在,我们要 tackling 一个非常常见的需求:如何获取用户在表单输入框(<input>
)里输入的内容?比如,在一个登录页面,我们需要拿到用户输入的用户名和密码。
欢迎来到React学习的第七站!
在传统的HTML和jQuery中,我们通常是在需要的时候(比如点击提交按钮时)才去DOM中“读取”输入框的值。但在React中,我们有一种更推荐、更“React”的方式,叫做受控组件 (Controlled Components)。
什么是受控组件?
简单来说,一个受控组件就是:一个表单元素(如<input>
, <textarea>
, <select>
),它的值完全由React的State来“控制”。
它的工作流程是这样的:
- 我们用
useState
创建一个 State 变量,用来存储输入框的值。 - 我们把这个 State 变量的值,通过
value
属性,赋给输入框。 - 我们给输入框绑定
onChange
事件。每当用户在输入框里打字时,onChange
事件就会被触发。 - 在
onChange
的事件处理函数里,我们获取输入框的最新值,然后调用setState
函数来更新我们的 State。 - 由于 State 发生了变化,React 会重新渲染组件,输入框的
value
也随之更新,显示出最新的内容。
听起来有点绕?别担心,我们通过一个实战案例来走一遍,你马上就会明白。
核心思想: 输入框里显示什么,不再是输入框自己说了算,而是由React的State说了算。实现了数据(State)和视图(UI)的完全同步。
实战:创建一个简单的用户名输入框
我们的目标是:创建一个输入框,用户在里面输入的任何内容,都会被实时地显示在页面的另一个地方。
第一步:准备App.jsx
老规矩,我们先搭好架子,并用useState
创建一个username
的State,初始值为空字符串 ''
。
import { useState } from 'react';
import './App.css';function App() {const [username, setUsername] = useState('');return (<div><h1>受控组件入门</h1><label>用户名:{/* 我们将在这里创建受控的 input */}</label><hr /><h3>你输入的内容是: {username}</h3></div>);
}export default App;
第二步:创建受控的<input>
现在,我们来创建这个输入框,并把它和我们的username
State关联起来。
修改return
部分的代码:
function App() {const [username, setUsername] = useState('');// 3. 定义 onChange 事件处理函数const handleUsernameChange = (event) => {// 4. 从事件对象中获取输入框的最新值const newValue = event.target.value;// 5. 更新 statesetUsername(newValue);};return (<div><h1>受控组件入门</h1><label>用户名:<input type="text" // 1. 将 state 的值绑定到 input 的 valuevalue={username}// 2. 监听 input 的内容变化onChange={handleUsernameChange}/></label><hr /><h3>你输入的内容是: {username}</h3></div>);
}
代码解释(请严格按照数字顺序理解):
value={username}
: 我们把username
这个State的值,强制赋给了输入框的value
属性。现在,这个输入框显示什么,完全由username
决定。onChange={handleUsernameChange}
: 我们监听输入框的change
事件。只要用户在里面打一个字,这个事件就会被触发,并调用handleUsernameChange
函数。- 我们定义了
handleUsernameChange
函数。这个函数会自动接收一个事件对象 (event) 作为参数。 event.target.value
: 这是获取输入框当前值的标准方式。event.target
指向触发事件的DOM元素(也就是那个<input>
),.value
就是它的值。setUsername(newValue)
: 我们调用更新函数,把获取到的新值赋给username
这个State。
完整的流程闭环了!
用户打字 -> onChange
触发 -> handleUsernameChange
执行 -> setUsername
更新State -> React重新渲染 -> 输入框的value
被更新为最新的State值。
第三步:测试效果
保存文件,回到浏览器。现在,尝试在输入框里打字。你会发现,下面的“你输入的内容是:”那一行,会实时地、一字不差地显示出你输入的内容。
你已经成功掌握了受控组件!
为什么推荐使用受控组件?
- 唯一数据源 (Single Source of Truth): 组件的状态都由React State统一管理,代码逻辑更清晰,调试更方便。
- 实时校验: 因为你能实时拿到输入值,所以可以轻松地做一些即时校验,比如限制输入长度、禁止输入特殊字符等。
- 动态交互: 可以根据输入的值,动态地改变其他UI元素的状态,比如当密码长度小于6位时,提交按钮置灰。
处理多个输入框
如果一个表单有多个输入框(比如用户名和密码),难道要为每个输入框都写一个单独的事件处理函数吗?当然不用!我们可以用一个更巧妙的方式来处理。
实战:一个简单的登录表单
import { useState } from 'react';
import './App.css';function App() {const [form, setForm] = useState({username: '',password: ''});const handleChange = (event) => {const { name, value } = event.target; // 解构获取 name 和 valuesetForm(prevForm => ({...prevForm, // 复制旧的 form 对象[name]: value // 使用计算属性名动态更新对应的字段}));};const handleSubmit = (event) => {event.preventDefault(); // 阻止表单默认的提交刷新行为alert(`登录中... 用户名: ${form.username}, 密码: ${form.password}`);};return (<form onSubmit={handleSubmit}><h1>登录表单</h1><div><label>用户名:</label><input type="text" name="username" value={form.username} onChange={handleChange} /></div><div><label>密码:</label><input type="password" name="password" value={form.password} onChange={handleChange} /></div><button type="submit">登录</button></form>);
}export default App;
代码解释:
- 我们用一个对象来作为State,
{username: '', password: ''}
。 - 给每个
<input>
添加了一个name
属性,且name
的值与State对象中的键名完全对应。 - 我们只用了一个
handleChange
函数。在函数内部,通过event.target.name
可以知道是哪个输入框触发了事件,event.target.value
是它的值。 [name]: value
这是JavaScript的计算属性名语法,它允许我们动态地设置对象的键。如果name
是"username"
,这就相当于username: value
。- 我们用
<form>
和onSubmit
来处理表单的提交。event.preventDefault()
是防止页面刷新的关键。
总结与思考
今天,我们攻克了React中非常重要的一个环节——表单处理。你已经掌握了:
- 受控组件的核心概念:UI的值由React State控制。
- 如何使用
useState
,value
和onChange
三件套,实现对单个输入框的控制。 - 如何用一个State对象和一个事件处理函数,优雅地管理多个输入框。
现在,我们的应用已经可以和用户进行深度的数据交互了。但是,我们所有的数据都还停留在前端。一个真正的应用,需要和后端服务器进行通信,去获取和提交数据。
在下一篇文章 《React副作用处理:useEffect入门,组件加载后如何请求API数据?》 中,我们将学习如何使用useEffect
这个强大的Hook,在组件渲染完成后去执行一些“副作用”操作,比如从一个公开的API获取数据并展示在页面上。我们下期再会!