淺析React中的受控元件和非受控元件
React中的受控和非受控表單並不複雜
你可能看到過很多文章,說不應該使用setState
,refs
不好……這是自相矛盾的。很難理解怎樣是對的,甚至連選擇的規則都不清楚。
那我們應該怎麼解決表單這個棘手的問題呢?
畢竟表單還是某些web應用的核心。可是處理表單好像變成了絆腳石?
不用害怕了。我會告訴你這兩種方式的區別和使用場景。
The Uncontrolled
非控表單輸入類似於傳統的HTML表單輸入:
class Form extends Component{
render(){
return (
<div>
<input type="text" />
</div>
);
}
}
他們記得你輸入了什麼。你可以使用ref獲取他們的值。例如,在按鈕的onClick處理方法中:
class Form extends Component {
handleSubmitClick = () => {
const name = this._name.value;
// do something with `name`
}
render() {
return (
<div>
<input type="text" ref={input =>this._name = input} />
<button onClick={this.handleSubmitClick}>Sign up</button>
</div>
);
}
}
換句話說,你要在自己需要的時候從表單域中拉取相應的值。這可能發生在表單提交的時候。
那是實現表單輸入的最簡單的方法。下面是一些有效的使用場景:在現實世界中的簡單表單,還有在學React的時候。
不過它並不是很強大,接下來讓我們看看受控表單。
The Controlled
受控表單接收當前值作為屬性,或者作為改變其值的呼叫。可以說它是一種更“React”的方式(但並不意味著總是用它)。
<input value={someValue} onChange={handleChange} />
這很好,但是輸入的值不得不儲存在某個地方的state裡。典型的是,渲染input(又稱表單元件)的元件要在其state中儲存該值:
class Form extends Component {
constructor() {
super();
this.state = {
name: '',
};
}
handleNameChange = (event) => {
this.setState({ name: event.target.value });
};
render() {
return (
<div>
<input
type="text"
value={this.state.name}
onChange={this.handleNameChange}
/>
</div>
);
}
}
(當然,還可能儲存在其他元件的state裡,或者一些單獨的狀態儲存像Redux中。)
每次輸入新字元時,都會呼叫handleNameChange
。它接收輸入的新值,並把它放到state中。
- 首先是空字串
- 當你輸入a時,
handleNameChange
會取得a,並呼叫setState
。輸入表單的值就會重渲染為a。 - 你輸入了b,
handleNameChange
獲取到ab的值,並把它設定到state中。然後輸入域的值就再一次被渲染成ab。
這種方式把值推送給表單元件,因此Form元件總是會有當前輸入的值,而不必顯示請求。
這意味著你的資料(state)和UI(inputs)總是同步的。狀態給輸入元件以值 ,使用者的輸入又促使Form元件改變當前的值。
這也意味著表單元件會立即響應輸入的改動,如以下場景:
- 及時反饋,像驗證
- 使按鈕處於不可用狀態,除非表單域都有有效值
- 強制特定輸入格式,像信用卡號
但是如果你不需要這些,那就考慮更簡單的非受控元件吧。
What makes an element “controlled”
當然有其他表單元素。你可能使用多選框、單選按鈕、選擇列表和文字域等。
當通過prop設定這些元件的值時,他們就變成了受控元件。
不過,元件使用不同的屬性值來設定他們的值,這總結了一個表格:
Element | Value property | Change callback | New value in the callback |
---|---|---|---|
<input type="text" /> |
value="string" |
onChange |
event.target.value |
<input type="checkbox" /> |
checked={boolean} |
onChange |
event.target.checked |
<input type="radio" /> |
checked={boolean} |
onChange |
event.target.checked |
<textarea /> |
value="string" |
onChange |
event.target.value |
<select /> |
value="option value" |
onChange |
event.target.value |
Conclusion
受控和非受控表單域各有各的優勢。具體情況具體分析來選擇適合自己的方式。
如果你的表單非常簡單,只依賴UI反饋,帶refs的非受控元件就可以完成的很好,而不用在意那些說這樣不好的文章。
而且,這並不是一次性決定,你可以總是遷移到受控輸入上。從非受控元件遷移到受控元件並不難。