系列回顧:
在前面的文章中,我們已經掌握了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獲取數據并展示在頁面上。我們下期再會!