本文轉載自:衆成翻譯
譯者:iOSDevLog
連接:http://www.zcfy.cc/article/3824
原文:https://www.fullstackreact.com/30-days-of-react/day-6/react
今天咱們開始瞭解React中有狀態組件的工做原理,而且看看咱們什麼時候以及爲何要使用狀態。函數
咱們幾乎完成了在React開始運行的第一週。咱們經過JSX工做,構建咱們的第一個組件,設置父子關係,並使用React驅動組件屬性。咱們還有一個重要的想法,咱們尚未討論React _狀態_相關的知識。性能
狀態
的事React並無讓咱們修改this.props
咱們有充分的理由的組件。想象一下,若是咱們將 title
屬性支持傳遞給Header
組件,而且Header
組件可以修改它。咱們如何知道title
是Header
組件的什麼 ?咱們設置了競爭條件,混亂的數據狀態,而且將是一個全面的壞主意,修改由父組件傳遞給咱們的變量並在小孩中修改。測試
然而,有時組件須要可以更新本身的狀態。例如,active
在秒錶上設置標誌或更新計時器。this
雖然最好props
儘量多地使用,但有時咱們須要堅持組件的狀態。爲了處理這個問題,React使咱們有能力在組件中擁有_狀態_。spa
組件裏的state
意圖徹底是內部的組件,它的孩子(即組件和任何孩子使用它訪問)。相似於咱們如何props
在組件中訪問,能夠經過this.state
組件訪問狀態。不管什麼時候狀態改變(經過 this.setState()
),組件將從新投遞。翻譯
例如,假設咱們有一個簡單的時鐘組件來顯示當前時間:code
即便這是一個簡單的時鐘組件,它確實保留狀態,由於它須要知道當前顯示的時間。沒有使用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不只出於性能緣由,最好使用,可是由於有狀態的組件更難測試。
今天,咱們更新了咱們的組件以使其處於狀態狀態,如今有必要處理如何使組件成爲狀態。明天咱們將進入組件的生命週期,什麼時候/如何與頁面進行交互。
上面提到的組件在代碼庫中,只是爲菜單按鈕提供了一個很好的顯示。
const MenuButton = (props) => ( <div className="menuIcon"> <div className="dashTop"></div> <div className="dashBottom"></div> <div className="circle"></div> </div> )