iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 21
1
Modern Web

I Want To Know React系列 第 21

I Want To Know React - Controlled component 語法

回顧 React form & Controlled component

上個章節中,我們學習到了在 React 中可以如何處理 form element 的兩種方式:

  • Controlled component:由 React 管控 HTML form element 的 state
  • Uncontrolled component:由 HTML form element 自行管理 state

也知道了 controlled component 的使用情境有以下幾種:

  • 需要對 form 檢查是否修改過(dirty)、格式驗證(validation)...etc 等狀況時
  • 需要取得 form 的 state 或內容去連動修改其他 component 時

接下來就來詳細介紹 controlled component 的語法吧!

Controlled component 語法

如上個章節所述,controlled component 的使用重點就在於:

  • 設定 value prop
  • 設定 onChange prop
  • 取得使用者輸入的新值,並使用 setState 更新 value 的值

然而每個 form element 用來設定 value 的 prop 與取得使用者輸入值的方式不盡相同,因此下面將一一介紹各 element 的 value prop、onChange、取得使用者輸入值的方式以及其用法。

Text input controlled component 語法

Text input controlled component 的語法重點如下:

  • value prop: value
  • onChange prop:onChange
  • 使用者輸入的新值:event.target.value

讓我們來看看使用範例:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {value: ''};

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('A name was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Name:
          <input type="text" value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

使用者也可以到 CodePen 上實際試試。

這個範例可以輸入名字,按下 submit 後名字就會透過 alert 顯示到畫面上。

可以看到程式中,value={this.state.value} 被設定在 input element 上,也就是 input element 的顯示值會一直等同於 this.state.value

接著,當使用者輸入任何字元時,都會觸發 onChange 的 handler:this.handleChange

this.handleChange 中會執行 setState 把使用者輸入的內容:event.target.value 更新到 this.state.value 上,使用者因此可以看到 input 上顯示自己剛剛輸入的值。

最後,當 input 按下 submit 時,就會觸發 form 的 onSubmit={this.handleSubmit} 並用 alert 將使用者的名字顯示到畫面上。

Checkbox input controlled component 語法

Checkbox input controlled component 的語法重點如下:

  • value prop: checked
  • onChange prop:onChange
  • 使用者輸入的新值:event.target.checkedevent.target.value

較特別的地方是,checkbox 使用 event.target.checkedevent.target.value 都可以拿到使用者輸入的新值。

使用 event.target.checked 拿到的會是 Boolean,代表使用者勾選與否。通常會在 input 中加上 name prop 以取得 event.target.name 來設定對應的 state 內容:

class SkillForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = { React: false, Vue: false, Angular: false };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

	handleChange(event) {
    this.setState({ [event.target.name]: event.target.checked });
  }

	// ...

  render() {
    return (
			//...
      <input
				type="checkbox"
				name="React"
				checked={this.state.React}
				onChange={this.handleChange}
			/>
  }
}

使用者可以到 CodePen 查看範例。

而使用 event.target.value 拿到的會是 String,代表使用者勾選選項的值。這個作法需要在 input 中加上 value prop 來設定對應的 state 內容。

使用方式與 event.target.checked 大同小異,讀者可以查看這個 CodePen 範例

以上兩個做法都是可行的,讀者可視當下的使用情境決定要用哪個作法。

Radio input controlled component 語法

Radio input controlled component 的語法重點如下:

  • value prop: checked
  • onChange prop:onChange
  • 使用者輸入的新值:event.target.checkedevent.target.value

type=checkbox 相同,input type=radio 使用 event.target.checkedevent.target.value 都可以拿到使用者輸入的新值。

使用 event.target.checked 拿到的會是 Boolean,代表使用者勾選與否:

radio 與 checkbox 相同,使用 event.target.checkedevent.target.value 都可以拿到使用者輸入的新值。

使用 event.target.checked 拿到的會是 Boolean,代表使用者勾選與否。通常會在 input 中加上 name prop 以取得 event.target.name 來設定對應的 state 內容:

class GenderForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = { gender: "" };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({ gender: event.target.name });
  }

	// ...

  render() {
    return (
			// ...
      <input
				type="radio"
				name="Male"
				checked={this.state.gender === "Male"}
				onChange={this.handleChange}
				/>
    );
  }
}

使用者可以到 CodePen 查看範例。

而使用 event.target.value 拿到的會是 String,代表使用者勾選選項的值。這個作法需要在 input 中加上 value prop 來設定對應的 state 內容。

使用方式與 event.target.checked 大同小異,讀者可以查看這個 CodePen 範例

以上兩個做法都是可行的,讀者可視當下的使用情境決定要用哪個作法。

Textarea controlled component 語法

Textarea controlled component 的語法重點如下:

  • value prop: value
  • onChange prop:onChange
  • 使用者輸入的新值:event.target.value

在 HTML 中,textarea 是以 children 定義預設內容:

<!-- HTML textarea -->
<textarea>
  Hello there, this is some text in a text area
</textarea>

JSX 的 textarea 為了與 input text 的 prop api 統一,則是使用 value 作為設定輸入內容的 prop:

// JSX controlled textarea
<textarea value={this.state.value} onChange={this.handleChange} />

讀者也可以到 CodePen 上查看完整範例。

Select controlled component 語法

Select controlled component 的語法重點如下:

  • value prop: value(內容為 option 的 value)
  • onChange prop:onChange
  • 使用者輸入的新值:event.target.value

在 HTML 中,select 定義預設選定內容的方式是在 children 的目標 option 中加上 selected prop:

<!-- HTML select -->
<select>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option selected value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>

在 JSX 中,則是在 select 中加上 value prop 來設定輸入內容,這樣就只需要更新一個地方就可以控制所有 option 的選取與否。另外,也能與 input text 的 prop api 統一格式:

// JSX controlled select
<select value={this.state.value} onChange={this.handleChange}>
  <option value="grapefruit">Grapefruit</option>
  <option value="lime">Lime</option>
  <option value="coconut">Coconut</option>
  <option value="mango">Mango</option>
</select>

讀者也可以到 CodePen 上查看完整範例。

語法整理表

這篇教學文將每個 form element 的 value prop / onChange / 使用者輸入的新值整理成了一個淺顯易懂的表格,讀者應該會覺得很有幫助:

Form element value prop onChange prop 使用者輸入的新值
<input type="text" /> value="string" onChange event.target.value
<input type="checkbox" /> checked={boolean} onChange event.target.checkedevent.target.value
<input type="radio" /> checked={boolean} onChange event.target.checkedevent.target.value
<textarea /> value="string" onChange event.target.value
<select /> value="option value" onChange event.target.value

Controlled component 使用注意事項

React 用 value 來控制 input

需要注意的是,只要 form element 只要設定了 value prop 就算是 controlled component 了。React 會把 input 的內容設定為 value 的值。

然而不同時設定 onChange 的話,controlled component 的值就會永遠被固定住無法被使用者輸入改變,如下所示:

// This input in uneditable
<input value="uneditable" />

因此正常來講 value 跟 onChange prop 都要同時設定。

Value 為 Nil 時可正常輸入

一個 controlled component 的特例是,如果 value 是 Nilnullundefined)的話,該 component 就會變成 uncontrolled component,React 就不會控制其輸入內容。使用者輸入的內容會不經過 React 的掌控顯示到畫面上,範例如下:

// The bellow two inputs are all editable
<input />
<input value={undefined} />
<input value={null} />
// Warning: value prop on input should not be null. Consider using an empty string to clear the component or undefined for uncontrolled components.

這時使用者可以自由輸入內容。而 React 也會顯示 value 不應該是 null 的警告。

Controlled component 使用技巧

設定 form 預設值

還記得前面段落提到的,controlled component 的內容是與 React component state 同步的。

也就是說,無論何時設定 state,其內容都會顯示到 form 上。

我們可以利用這個特性,在 component 初始化時,將 this.state 的值時設定為要 form 預設顯示的內容。Form 就會因此在一 mount 起來時就帶有預設值。

舉例來說,如果要用 textarea 做一個簡單的作文輸入器,而這個 textarea 要有預設值:

class EssayForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      value: 'Please write an essay about your favorite DOM element.'
    };

    this.handleChange = this.handleChange.bind(this);
    this.handleSubmit = this.handleSubmit.bind(this);
  }

  handleChange(event) {
    this.setState({value: event.target.value});
  }

  handleSubmit(event) {
    alert('An essay was submitted: ' + this.state.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          Essay:
          <textarea value={this.state.value} onChange={this.handleChange} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

使用者可以到 CodePen 上查看完整範例。

範例中在 constructor 中初始 this.statevalue,並將 textareavalue 設定為 this.state.value,如此當 EssayForm 被 mount 起來時使用者就會看到預設 state 'Please write an essay about your favorite DOM element.' 顯示在輸入框上了。

使用 name 同時管理多個 input

當同時有多個 input 時也可以在 form element 中加入 name prop,如此就可以 onChange callback 就透過 event.target.name 設定對應的 state 資料,以達到同時管理多個 input 的功能:

class NameForm extends React.Component {
  constructor(props) {
    super(props);
    this.state = { text: 'initial value' };
  }

  changeInputValue = (e) => {
    this.setState({ [e.target.name]: e.target.value });
  }

  render() {
    return (
      <div>
        <input type='text' name='firstName' value={this.state.firstName} onChange={this.changeInputValue} />
        <input type='text' name='lastName' value={this.state.lastName} onChange={this.changeInputValue} />
      </div>
    );
  }
}

讀者也可以到 CodePen 上查看範例。

範例中,因為有為兩個 input 都設定 name prop,因此當 onChange 觸發 changeInputValue 時,就可以透過 { [e.target.name]: e.target.value } 去更新使用者輸入的 input 的 state。

React Form 相關 library

如果想要使用現成的解決方案來支援 React form 修改檢查(dirty)、格式驗證(validation)、追蹤拜訪欄位(visit)等功能的話,也可以考慮使用以下這些 library:

小結

這個章節中介紹了各種 controlled form element 的語法,包括:

  • Text input
  • Checkbox input
  • Radio input
  • Textarea
  • Select

另外也了解了 controlled component 的使用注意事項:

  • React 用 value 控制 input
  • Value 為 Nil 時可正常輸入

還有知道了 controlled component 的使用技巧:

  • 設定 form 預設值
  • 使用 name 同時管理多個 input

參考資料


上一篇
I Want To Know React - 初探 Form & Controlled component
下一篇
I Want To Know React - Uncontrolled component
系列文
I Want To Know React30

尚未有邦友留言

立即登入留言