【全棧React】第6天: 狀態

本文轉載自:衆成翻譯
譯者: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 組件可以修改它。咱們如何知道titleHeader 組件的什麼 ?咱們設置了競爭條件,混亂的數據狀態,而且將是一個全面的壞主意,修改由父組件傳遞給咱們的變量並在小孩中修改。測試

然而,有時組件須要可以更新本身的狀態。例如,active 在秒錶上設置標誌或更新計時器。this

雖然最好props 儘量多地使用,但有時咱們須要堅持組件的狀態。爲了處理這個問題,React使咱們有能力在組件中擁有_狀態_。spa

組件裏的state 意圖徹底是內部的組件,它的孩子(即組件和任何孩子使用它訪問)。相似於咱們如何props 在組件中訪問,能夠經過this.state 組件訪問狀態。不管什麼時候狀態改變(經過 this.setState() ),組件將從新投遞。翻譯

例如,假設咱們有一個簡單的時鐘組件來顯示當前時間:code

即便這是一個簡單的時鐘組件,它確實保留狀態,由於它須要知道當前顯示的時間。沒有使用state,咱們能夠設置一個計時器並從新渲染整個React組件,但頁面上的其餘組件可能不須要從新渲染…這將是一個頭痛的問題。對象

相反,咱們能夠設置一個計時器來調用組件內部的rerender並更改此組件的內部狀態。生命週期

咱們來創建這個組件。首先,咱們將建立咱們將要調用的組件Clock。在進入狀態以前,咱們來構建組件並建立該render() 函數。咱們須要考慮數字,若是數字小於10,在數字前面加上一個零(0),並進行相應的設置 am/pmrender()函數的最終結果可能以下所示:

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.stateClock 組件中有一個定義,咱們能夠在 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>
)
相關文章
相關標籤/搜索