本文轉載自:眾成翻譯
譯者:iOSDevLog
連結:http://www.zcfy.cc/article/3824
原文:https://www.fullstackreact.com/30-days-of-react/day-6/
今天我們開始瞭解React中有狀態元件的工作原理,並且看看我們何時以及為什麼要使用狀態。
我們幾乎完成了在React開始執行的第一週。我們通過JSX工作,構建我們的第一個元件,設定父子關係,並使用React驅動元件屬性。我們還有一個重要的想法,我們還沒有討論React _狀態_相關的知識。
有關狀態
的事
React並沒有讓我們修改this.props
我們有充分的理由的元件。想象一下,如果我們將 title
屬性支援傳遞給Header
元件,並且Header
元件能夠修改它。我們如何知道title
是Header
元件的什麼 ?我們設定了競爭條件,混亂的資料狀態,並且將是一個全面的壞主意,修改由父元件傳遞給我們的變數並在小孩中修改。
然而,有時元件需要能夠更新自己的狀態。例如,active
在秒錶上設定標誌或更新計時器。
雖然最好props
儘可能多地使用,但有時我們需要堅持元件的狀態。為了處理這個問題,React使我們有能力在元件中擁有_狀態_。
元件裡的state
意圖完全是內部的元件,它的孩子(即元件和任何孩子使用它訪問)。類似於我們如何props
在元件中訪問,可以通過this.state
元件訪問狀態。無論何時狀態改變(通過 this.setState()
),元件將重新投遞。
例如,假設我們有一個簡單的時鐘元件來顯示當前時間:
即使這是一個簡單的時鐘元件,它確實保留狀態,因為它需要知道當前顯示的時間。沒有使用state,我們可以設定一個計時器並重新渲染整個React元件,但頁面上的其他元件可能不需要重新渲染…這將是一個頭痛的問題。
相反,我們可以設定一個計時器來呼叫元件內部的rerender並更改此元件的內部狀態。
我們來建立這個元件。首先,我們將建立我們將要呼叫的元件Clock
。在進入狀態之前,我們來構建元件並建立該render()
函式。我們需要考慮數字,如果數字小於10,在數字前面加上一個零(0
),並進行相應的設定 am/pm
。 render()
函式的最終結果可能如下所示:
class Clock extends React.Component {
render() {
const currentTime = new Date(),
hours = currentTime.getHours(),
minutes = currentTime.getMinutes(),
seconds = currentTime.getSeconds(),
ampm = hours >= 12 ? `pm` : `am`;
return (
<div className="clock">
{
hours == 0 ? 12 :
(hours > 12) ?
hours - 12 : hours
}:{
minutes > 9 ? minutes : `0${minutes}`
}:{
seconds > 9 ? seconds : `0${seconds}`
} {ampm}
</div>
)
}
}
// ...
export default Clock
如果我們渲染我們的新Clock
元件,我們只會在元件本身重新執行時獲得時間。這不是一個非常有用的時鐘(還)。為了將靜態時間顯示Clock
元件轉換為顯示時間的時鐘,我們需要每秒更新一次。
為了做到這一點,我們需要跟蹤元件狀態下的_current_ 時間。 為此,我們需要設定初始狀態值。 在ES6類樣式中,我們可以通過將this.state
設定為一個值來設定constructor()
中元件的初始狀態。
constructor(props) {
super(props);
this.state = this.getTime();
}
建構函式的第一行應該_始終_呼叫
super(props)
。如果您忘記了這一點,元件將不會非常喜歡(即會有錯誤)。
現在我們this.state
在Clock
元件中有一個定義,我們可以在 render()
函式中引用它this.state
。讓我們更新我們的 render()
函式this.state
來獲取以下值:
class Clock extends React.Component {
// ...
render() {
const {hours, minutes, seconds, ampm} = this.state;
return (
<div className="clock">
{
hours === 0 ? 12 :
(hours > 12) ?
hours - 12 : hours
}:{
minutes > 9 ? minutes : `0${minutes}`
}:{
seconds > 9 ? seconds : `0${seconds}`
} {ampm}
</div>
)
}
}
我們現在可以更新 state
元件而不是直接使用資料值。為了更新狀態,我們將使用該函式 this.setState()
,這將觸發元件重新渲染。
在我們的Clock
元件中,我們使用本機setTimeout()
JavaScript函式建立一個定時器,以this.state
在1000毫秒內更新物件。我們將把這個功能放在一個函式中,我們再次呼叫它。
class Clock extends React.Component {
// ...
constructor(props) {
super(props);
this.state = this.getTime();
}
// ...
setTimer() {
clearTimeout(this.timeout);
this.timeout = setTimeout(this.updateClock.bind(this), 1000);
}
// ...
updateClock() {
this.setState(this.getTime, this.setTimer);
}
// ...
}
我們將在下一節中介紹生命週期中的鉤子,但是為了簡單起見,我們暫時將其簡稱為
constructor()
。
在該 updateClock()
函式中,我們將要在新時間內更新狀態。我們現在可以在 updateClock()
函式中更新狀態:
class Clock extends React.Component {
// ...
updateClock() {
this.setState(this.getTime, this.setTimer);
}
// ...
}
該元件將安裝在頁面上,並在(大約)一秒鐘(1000毫秒)內更新當前時間。但是,它不會再重新設定。我們可以在setTimer()
函式結束時再次呼叫該函式:
class Clock extends React.Component {
// ...
updateClock() {
const currentTime = new Date();
this.setState({
currentTime: currentTime
})
this.setTimer();
}
// ...
}
現在,元件本身可能會比超時功能再次呼叫慢,這將導致重新出現的瓶頸,並且不必要地在移動裝置上使用寶貴的電池。在呼叫setTimer()
函式之後this.setState()
,我們可以將第二個引數傳遞給this.setState()
函式,該函式將在狀態更新_後_保證被呼叫。
class Clock extends React.Component {
// ...
updateClock() {
const currentTime = new Date();
this.setState({
currentTime: currentTime
}, this.setTimer);
}
// ...
}
更新我們的活動列表
我們可以Header
在上一節中我們一直在研究的活動列表中更新我們的元件。當使用者點選search
圖示,我們將要顯示<input>
元件。
嘗試一下!點選下面的搜尋圖示:(想要有效果還是去原文體驗吧?)
知道我們現在知道的是,現在 this.state
我們可以更新檢視來新增條件呈現<input>
class Header extends React.Component {
constructor(props) {
super(props);
this.state = {
searchVisible: false
}
}
// toggle visibility when run on the state
showSearch() {
this.setState({
searchVisible: !this.state.searchVisible
})
}
render() {
// Classes to add to the <input /> element
let searchInputClasses = ["searchInput"];
// Update the class array if the state is visible
if (this.state.searchVisible) {
searchInputClasses.push("active");
}
return (
<div className="header">
<MenuButton />
<span className="title">
{this.props.title}
</span>
<input
type="text"
className={searchInputClasses.join(` `)}
placeholder="Search ..." />
{/* Adding an onClick handler to call the showSearch button */}
<div
onClick={this.showSearch.bind(this)}
className="fa fa-search searchIcon"></div>
</div>
)
}
}
有些事情要記住
- 當我們呼叫
this.setState()
一個物件引數時,它將執行一個資料的_淺合併_到可用的物件中this.setState()
,然後重新渲染元件。 - 我們通常只想在我們的狀態中保持我們將在該
render()
函式中使用的值。從上面我們的時鐘的例子,請注意,我們的儲存hours
,minutes
,以及seconds
在我們的狀態。在我們不打算在render功能中使用的狀態下儲存物件或計算通常是一個壞主意,因為它可能導致不必要的渲染和浪費的CPU週期。
正如我們在本節頂部指出的那樣,props不僅出於效能原因,最好使用,但是因為有狀態的元件更難測試。
今天,我們更新了我們的元件以使其處於狀態狀態,現在有必要處理如何使元件成為狀態。明天我們將進入元件的生命週期,何時/如何與頁面進行互動。
MenuButton
上面提到的元件在程式碼庫中,只是為選單按鈕提供了一個很好的顯示。
const MenuButton = (props) => (
<div className="menuIcon">
<div className="dashTop"></div>
<div className="dashBottom"></div>
<div className="circle"></div>
</div>
)