React從0到1系列第三章——深刻理解React組件的組合、props和state

1、組件的組和、組件樹

想要理解數據是如何在組件樹內自上往下流動(Data Flow),須要先明白React組件的組合、嵌套和組件樹的構成。html

注意:自定義的組件都必需要用大寫字母開頭,普通的HTML標籤都用小寫字母開頭。react

下面是用React.js組建的房子爲例子來講明一下組件的組和應用。一個房子裏面有一個房間和一個廁所。房間裏有一臺電腦和兩條狗,以下:git

// 電腦
class Computer extends Component {  render () {  return <h3>this is Computer</h3>  } }  // 狗 class Dog extends Component {  render () {  return <h3>this is Dog</h3>  } }  // 房間 class Room extends Component {  render () {  return (  <div>  <h2>this is Room</h2>  <Computer />  <Dog />  <Dog />  </div>  )  } }  // 廁所 class WC extends Component {  render () {  return <h2>this is WC</h2>  } }  // 房子 class House extends Component {  render () {  return <div>  <h1>this is House</h1>  <Room />  <WC />  </div>  } } 複製代碼

最後頁面會輸出以下圖:github

組件能夠和組件組合在一塊兒,組件內部可使用別的組件。這樣的組合嵌套,最後構成一個所謂的組件樹。用下圖表示這種樹狀結構它們之間的關係:web

2、組件的state

state是組件的當前狀態,React根據狀態state呈現不一樣的UI展現。React.jsstate 就是用來存儲這種可變化的狀態的。一旦狀態(數據)更改,組件就會自動調用render從新渲染 UI,這個更改的動做經過this.setState()方法來觸發。數組

下面是一個簡單的修改狀態的小例子:異步

class Like extends Component {
 constructor(props) {  super(props)  this.state = { isLike: false }  }  handleClick () {  this.setState({  isLike: !this.state.isLike  })  }  render () {  return (  <h3 onClick={this.handleClick.bind(this)}>  你{this.state.isLike ? '喜歡' : '不喜歡'}她  </h3>  )  } } 複製代碼

isLike存放在實例的state 對象當中,這個對象在構造函數裏面初始化。在這個組件的render函數內,會根據組件的state 的中的isLike的不一樣顯示喜歡或不喜歡。setState()方法由父類Component所提供。編輯器

正確的使用statesetState()方法

setState()方法,它接受一個對象或者函數做爲參數。函數

  • 對象做爲參數時
handleClick () {
 // 只須要傳入須要更新的部分,而不須要入整個對象。  this.setState({  isLike: !this.state.isLike  }) } 複製代碼
  • 函數做爲參數時
handleClick () {
 this.setState(() => {  return { isLike: !this.state.isLike }  }) } 複製代碼

不能直接修改state

不能直接修改state,若是直接修改state,組件並不會從新觸發render方法。性能

// 錯誤
this.state.isLike = !this.state.isLike  // 正確 this.setState({  isLike: !this.state.isLike }) 複製代碼

state的更新是異步的

在調用setState()的時候React.js並不會立刻修改statesetState()只是把要修改的狀態放入一個更新隊列中。React會優化真正的執行時機。以下:

class AddCount extends Component {
 constructor(props) {  super(props)  this.state = { count: 0 }  }  handleClick () {  this.setState({ count: this.state.count + 1 })  // 0  console.log(this.state.count)  }  render () {  return (  <div>  {/* 1 */}  <h2>{this.state.count}</h2>  <button onClick={this.handleClick.bind(this)}>添加</button>  </div>  )  } }  複製代碼

當點擊添加按鈕的時候頁面輸出的是1,打印出來的倒是0。因此在調用setState()以後,this.state不會當即映射爲新的值。

解決方案是setState(updater, [callback])接收更新後能夠傳入一個回調函數,一旦setState()完成而且組件重繪以後,這個回調函數將會被調用。

handleClick () {
 this.setState({ count: this.state.count + 1 }, () => {  // 1  console.log(this.state.count)  }) } 複製代碼

setState()淺合併

React.js出於性能緣由,可能會將屢次setState()的狀態修改合併成一次狀態修改。因此不要依賴當前的setState()計算下個State。以下的一個計數器:

class AddCount extends Component {
 constructor(props) {  super(props)  this.state = { count: 0 }  }  handleClick () {  this.setState({ count: this.state.count + 1 })  this.setState({ count: this.state.count + 1 })  }  render () {  return (  <div>  <h2>{this.state.count}</h2>  <button onClick={this.handleClick.bind(this)}>添加</button>  </div>  )  } } 複製代碼

當點擊添加按鈕的時候,會發現頁面輸出的是1。雖然咱們setState()方法調用了兩次。是由於當咱們調用setState()修改組件狀態時,組件state的更新實際上是一個淺合併的過程,至關於:

Object.assign(
 previousState,  {count: state.count + 1},  {count: state.count + 1},  ... )  複製代碼

因此若是後續操做要依賴前一個setState()的結果的狀況下就要使用函數來做爲setState()參數。React.js會把上一個setState()的結果傳入這個函數,你就可使用上一個正確的結果進行操做,而後返回一個對象來更新state。

handleClick () {
 this.setState({ count: this.state.count + 1 })  this.setState((prevState) => {  // 1  console.log(prevState.count)  return { count: prevState.count + 1 }  }) }} 複製代碼

把上次操做setState()更新的值傳入到下一個setState()裏,就能夠正確的顯示count了。

stateImmutable(不可變性)

React官方建議把state看成是的Immutable(不可變性)對象,state中包含的全部狀態都應該是不可變對象。當state中的某個狀態發生變化,咱們應該從新建立這個狀態對象,而不是直接修改原來的狀態。

關於Immutable能夠參考Immutable詳解及React中實踐

  1. 若有一個數組類型的狀態 names,當向 name中添加一個名字時,使用數組的 concat方法或 ES6的擴展運算符:
// 1
this.setState(prevState => ({  names: prevState.names.concat(['lisi']) }))  // 2 this.setState(prevState => ({  names: [...prevState.names,'lisi'] }))  複製代碼

注意不要使用pushpopshiftunshiftsplice等方法修改數組類型的狀態,由於這些方法都是在原數組的基礎上修改,而concatslice等返回一個新的數組。

  1. 若有一個對象類型的狀態 person,爲了避免改變本來的對象,咱們可使用 Object.assign 方法或者對象擴展屬性:
// 1
function updatePerson (person) {  return Object.assign({}, person, { age: 20 }) }  // 2 function updatePerson (person) {  return {...person,age:20} }  複製代碼

建立新的狀態對象要避免使用會直接修改原對象的方法,而是使用能夠返回一個新對象的方法。

當處理深層嵌套對象時,以immutable(不可變)的方式更新會很麻煩。可使用一些Immutable的JS庫,如Immutable.js來簡化開發。

3、組件的props

組件是相互獨立、可複用的單元,一個組件可能在不一樣地方被用到。可是在不一樣的場景下對這個組件的需求可能會根據狀況有所不一樣,因此要針對相同的組件傳入不一樣的配置項。

React.jsprops就能夠幫助咱們達到這個效果。每一個組件均可以接受一個props參數,它是一個對象,包含了全部你對這個組件的配置。

下面是一個能夠經過傳入props來控制是否是能夠點擊修改喜歡或不喜歡的文字:

class Like extends Component {
 constructor(props) {  super(props)  this.state = { isLike: false }  }  handleClick () {  this.setState({  isLike: !this.state.isLike  })  }  render () {  const clickable = this.props.clickable  return (  <h3 onClick={clickable ? this.handleClick.bind(this) : () => { }} >  你{ this.state.isLike ? '喜歡' : '不喜歡'}她  </h3>  )  } }  class App extends Component {   render () {  return (  <div>  <Like clickable={true} />  <Like clickable={false} />  </div>  )  } } 複製代碼

組件內部是經過this.props的方式獲取到組件的參數的,若是this.props裏面有須要的屬性咱們就採用相應的屬性,沒有的話就用默認的屬性。在使用一個組件的時候,能夠把參數放在標籤的屬性當中,全部的屬性都會做爲props`對象的鍵值。

默認配置defaultProps

React.js也提供了一種方式defaultProps,能夠方便的作到默認配置。

class Like extends Component {
 static defaultProps = {  clickable: true  }  //... } 複製代碼

props 不可變

props一旦傳入進來就不能夠在組件內部對它進行修改。可是能夠經過父組件主動從新渲染的方式來傳入新的props,從而達到更新的效果。以下:

class App extends Component {
  constructor(props) {  super(props)  this.state = { clickable: false }  }   handleClickOnChange () {  this.setState({  clickable: !this.state.clickable  })  }  render () {  return (  <div>  <Like clickable={this.state.clickable} />  <Like clickable={this.state.clickable} />  <button onClick={this.handleClickOnChange.bind(this)}>  修改clickable  </button>  </div>  )  }  複製代碼

參考

https://zh-hans.reactjs.org/

https://juejin.im/entry/59522bdb6fb9a06b9a516113

https://github.com/camsong/blog/issues/3

https://github.com/lcxfs1991/blog/issues/8

http://huziketang.mangojuice.top/books/react/

https://dev.to/rleija_/master-the-art-of-react-state-and-props-in-5-minutes-3hao

http://product.dangdang.com/25249546.html

本文使用 mdnice 排版

相關文章
相關標籤/搜索