3-4 React 中表單的基本應用

本單元對應的專案分支為:convert-internet-speed


雖然現在畫面上的圖示還沒辦法正常顯示,但在這個單元中,我們會先來了解如何在 React 中取得使用者在表單所輸入的資料。本單元完成後,使用者可以透過在左邊的 input 欄位輸入 Mbps 的數值後,直接在右邊得到轉換後的 MB/s,如下圖所示:

Imgur

使用 input 提供使用者輸入資料的地方#

首先要能夠將 Mbps 轉換成 MB/s 之前, ㄧ定需要先知道使用者輸入了什麼內容,因此在網速單位轉換器中,在 .converter 區塊內有 <input /> 標籤是要讓使用者輸入的:

Imgur

在 input 元素上綁定 onChange 事件#

和之前學到綁定點擊事件的方式相同,若要監控使用者在 <input /> 欄位中輸入了什麼,可以使用 onChange 事件,可以在 <input /> 內加上 onChange,在後面的 {} 內透過 console.log 來看看是否會觸發此事件,像是這樣:

<div className="flex-1">
<div className="converter-title">Set</div>
<input
type="number"
onChange={() => console.log('onChange')}
className="input-number"
min="0"
/>
</div>

現在當使用者在左側的對話框中輸入數字時,應該就可以在瀏覽器的 console 視窗中看到一直會跳出 onChange 的訊息。

透過 useState 讓 React 明白資料的變化#

現在雖然在使用者輸入對話框的訊息時,會觸發 onChange 事件,但和上一章中曾提過的一樣,只是監聽事件 React 並沒辦法得知它內部是否有任何資料改變,因此也不會知道是不是需要重新轉譯畫面。

為了要讓使用者在輸入左側的 Mbps 時,右側 MB/s 在畫面上的值能夠連動改變,因此需要把使用者輸入的內容透過 state 紀錄在 React 元件中,一但 React 發現這個 state 的內容有改變時,就會重新轉譯畫面。要在 React 元件中紀錄資料,就會用上一章使用過 useState 這個 React Hooks。

步驟一:載入 useState 方法#

和上一章中可以載入 useState 的方法不同,因為上一章是直接使用 CDN 載入 React 套件,這裡我們是把 React 套件下載到本機電腦上,因此要使用 React 或其內部的 useState 方法的話,都需要透過 import 的方式:

import React, { useState } from 'react';

步驟二:使用 useState 方法#

還記得 useState() 這個 React Hooks 的使用方式嗎?這個方法可以帶入參數當作預設值,呼叫之後會回傳兩個值,其中一個是 state,另一個是用來改變 state 的方法,因此在這裡我們可以把預設值設為 0,並取得 inputValuesetInputValue 這兩個回傳值:

function App() {
// 定義 state,取得預設值為 0 的 inputValue 和修改該狀態的 setInputValue 方法
const [inputValue, setInputValue] = useState(0);
return {
/* ...省略... */
};
}

步驟三:定義 onChange 的事件處理器#

在上個章節實作計數器的時候,我們是定義使用者點擊按鈕後的事件處理器,這裡則是要定義使用者輸入內容時的事件處理器,也就是要把使用者輸入的內容先透過 e.target.value 的方式取出來,然後透過 setInputValue 這個方法,請 React 幫我們更新 inputValue 這個 state 的資料。如此,當使用者輸入的資料有變更時,React 就能夠馬上知道:

function App() {
const [inputValue, setInputValue] = useState(0);
// 定義事件處理器
const handleInputChange = (e) => {
const { value } = e.target;
setInputValue(value);
};
return {
/* ...省略... */
};
}

:: tip 在 React 中,常使用 handle 當作事件處理器命名的開頭,例如 onClick 對應到 handleClickonChange 對應到 handleChange。 :::

步驟四:把 onChange 事件換成寫好的事件處理器#

最後,只需要把剛剛在 input 上寫的 onChange 事件改成這裡寫好的事件處理器即可。另外,要留意的地方是,這裡的 input 元素中,需要把 inputValue 這個 state 帶到 <input> 的 value 屬性中,像是這樣:

{
/* 畫面左側的 input */
}
<div className="flex-1">
<div className="converter-title">Set</div>
<input
type="number"
onChange={handleInputChange}
value={inputValue}
className="input-number"
min="0"
/>
</div>;

步驟五:讓畫面右邊的 input 值也同步更新#

在還沒有實作單位換算的功能之前,我們先讓右側的 input 欄位直接顯示使用者輸入的內容,如果能夠正常顯示的話,最後我們只需要做簡單的數學換算就可以了:

{/* 畫面右側的 input */}
<div className="text-right flex-1">
<div className="converter-title">Show</div>
<input
type="text"
className="input-number text-right"
disabled
value={inputValue}
/>
</div>
</div>

現在當使用者在左側輸入內容時,右側的數值就會跟著同步變動。

步驟六:加上單位換算的功能#

最後我們只需要加上單位換算的功能就完成了,在前面的單元中我們有提到 1 Mbps = 0.125 MB/s ,也就是 Mbps 的值除以 8 才會是 MB/s

imgur

因此,要正確的單位轉換,只需要修改右側帶入 <input /> 中的 value,讓它是使用者輸入的值除以 8 即可,也就是 value={inputValue/8}

{/* 畫面右側的 input */}
<div className="text-right flex-1">
<div className="converter-title">Show</div>
<input
type="text"
className="input-number text-right"
disabled
value={inputValue / 8}
/>
</div>
</div>

了解畫面更新的邏輯#

現在當使用者在左側輸入內容時(Mbps),右側就會馬上顯示換算好的網速(MB/s)。先前我們有提到,在 React 中是透過資料變動來驅動畫面更新,讓我們用同樣的觀念來思考一下這裡畫面是如何更新的。

使用者之所以能夠在畫面的右側看到自己輸入的內容,是因為下面這一連串過程導致畫面重新轉譯後,才把最新的 inputValue 顯示在使用者的畫面上:

Imgur

換你了:完成網速單位換算的功能#

現在要請你試著參考上面的步驟與說明,試著完成網速單位換算的功能,讓使用者在左側 Mbps 的欄位中輸入資料時,右側就會即時顯示對應 MB/s 的值,主要過程會包含:

  • 使用 useState 建立 React 狀態(state)
  • 定義 handleInputChange 事件,當使用者輸入內容時能夠更新資料狀態
  • handleInputChange 透過 onChange 和 input 進行綁定
  • 在畫面左側 input 欄位中透過 value 欄位以更新資料
  • 在畫面右側 input 欄位中透過 value / 8 的欄位以達到單位換算

畫面將會像這樣:

Imgur

現在網速單位換算的功能已經完成了,但接下來會先說明 React 中元件拆分和資料間傳遞的重要觀念,最後才來透過元件的方式,讓圖示能夠正確呈現在畫面。

本單元相關之網頁連結、完整程式碼與程式碼變更部分可於 convert-internet-speed 分支檢視,在這個單元中我們僅修改了 ./src/App.js 這支檔案,如果實作上有碰到任何問題的話,都可以到下面的連結檢視完整的程式碼:

https://github.com/pjchender/learn-react-from-hooks-internet-speed-converter/tree/convert-internet-speed

Imgur