(2024/04/06更新) 因應React在18後更新了許多不同的語法,更新後的教學之後將陸續放在 新的blog 中,歡迎讀者到該處閱讀,我依然會回覆這邊的提問
React 在輸入元素的處理上有一些比較特別的地方,先前我們已經使用過onClick()
,這篇我們來一次講全部輸入元素。
之前都用過。總之輸入事件都是用原生DOM API傳入的event去處理。event.target
是被點擊的對象,event.target.value
則是被點擊的對象的value。
<button value={123} onClick={(event)=>{ console.log(event.target.value)} } ></button>
每當輸入值一被改變,就會呼叫onChange
裡的函式,剩下都跟onClick一樣。
<select onChange={(e)=>{console.log(e.target.value)}}>
<option value="123">123</option>
<option value="456">456</option>
</select>
<form onSubmit={handleSubmit}>
<input type="submit" value="Submit" />
</form>
在過去,我們如果要給輸入元素初始值,要用value
。但在React的輸入元素中value
有別的用途,那我們要怎麼給初始值呢?
初始值改成了用defaultValue
這個屬性。你可以用下面這個範例來測試:
import React,{useState} from "react";
const InputForm=()=>{
const [word,setWord]=useState("初始文字");
return (
<>
<input
type="text"
defaultValue={word}
onChange={(e)=>{setWord(e.target.value)}}
/>
<div>word的值:{word}</div>
</>
)
}
export default InputForm;
那麼value
被改成了什麼呢? 答案是「input中目前的值」。舉例來說,如果你照剛剛一樣給defaultValue,然後用<input/>
來設定word這個state:
import React,{useState} from "react";
const InputForm=()=>{
const [word,setWord]=useState("初始文字");
return (
<>
<button onClick={(e)=>{setWord("")}}>清除word</button>
<input
type="text"
defaultValue={word}
onChange={(e)=>{setWord(e.target.value)}}
/>
<div>word的值:{word}</div>
</>
)
}
export default InputForm;
你會發現當你按下第一個按鍵時,input中的值並沒有跟隨著word而改變,這是因為defaultValue只和第一次渲染元素時有關。接著我們改把word綁在value
上:
import React,{useState} from "react";
const InputForm=()=>{
const [word,setWord]=useState("初始文字");
return (
<>
<button onClick={(e)=>{setWord("")}}></button>
<input
type="text"
value={word}
onChange={(e)=>{setWord(e.target.value)}}
/>
<div>word的值:{word}</div>
</>
)
}
export default InputForm;
此時input中的值就會始終跟隨著word。這樣用state綁定輸入元素的方式我們稱為控制組件。
當你想用Http Request取得的值去設定input初始值時,你應該要使用value而不是defaultValue。因為非同步特性,第一次渲染時你幾乎不可能拿到Http Request的Response,所以defaultValue就不可能變成Response data。
請注意因為當我們只寫名稱、不給props值時,React會預設認定該props為true
,所以以下兩者是一樣的:
<input type="text" disabled />
<input type="text" disabled={true} />
select元素的初始選取選項可以透過<option>
上的selected
來控制
<select onChange={(e)=>{setSelect(e.target.value)}}>
<option value="123">123</option>
<option selected={true} value="456" >456</option>
</select>
核取和選取的input都是用check
這個props來控制是否被選取
const [check,setCheck]=useState(false);
<input type="radio" checked={check} value="123" onChange={(e)=>{setCheck(true)}} />123<br/>
<input type="radio" checked={!check} value="456" onChange={(e)=>{setCheck(false)}} />456
當今天輸入的元素很多, 就可能變成元件中有一大堆的setState
。這樣不僅可讀性差,如果想要一次處理一堆輸入元素也很麻煩。
除了第三方插件外,React官方推薦的做法是給每個元素name
屬性。把所有元素綁定單一函式,並在函式中以event.target.name
來分流處理。
你會發現官方文件中class component的state是統一用this.setState
設定,所以可以用computed property name語法。但funtion component不行。
你可能會寫出類似這樣的架構:
import React from "react";
const handleMulInput=(e)=>{
if (e.target.name === "account"){
console.log("account is "+e.target.value);
} else if (e.target.name === "password"){
console.log("password is "+e.target.value);
}
}
const InputForm=()=>{
return (
<div>
<input
type="text"
name="account"
onChange={handleMulInput}
/>
<input
type="text"
name="password"
onChange={handleMulInput}
/>
</div>
)
}
export default InputForm;
但這樣做因為函式定義在非React function component內,這樣就不能使用React hook了。
那我們要把函式直接定義在function component內嗎? 怎麼樣取得元素內的值會比較好呢?
在接下來的這幾天,我們會依序提到React處理非控制組件的api、以及React效能處理的討論。到時候再回來談要如何處理這個case。