react.js 小書 學(xué)習(xí)筆記(一)

作者:胡子大哈
原文鏈接: http://huziketang.com/books/react/lesson1

看了 react.js 小書 第一階段的內(nèi)容,邊看邊做一點記錄,整理了一些知識點。僅供學(xué)習(xí)。

6.使用 JSX 描述 UI 信息

JSX 原理

React.js 把 JavaScript 的語法擴展了一下,讓 JavaScript 語言能夠支持這種直接在 JavaScript 代碼里面編寫類似 HTML 標(biāo)簽結(jié)構(gòu)的語法,這樣寫起來就方便很多了。編譯的過程會把類似 HTML 的 JSX 結(jié)構(gòu)轉(zhuǎn)換成 JavaScript 的對象結(jié)構(gòu)。

使用 React 和 JSX 的時候一定要經(jīng)過編譯的過程。

這里再重復(fù)一遍:所謂的 JSX 其實就是 JavaScript 對象。

JSX 是 JavaScript 語言的一種語法擴展,長得像 HTML,但并不是 HTML。JSX 在編譯的時候會變成相應(yīng)的 JavaScript 對象描述。

react-dom 負責(zé)把這個用來描述 UI 信息的 JavaScript 對象變成 DOM 元素,并且渲染到頁面上。

可以想象有一個叫 react-canvas 可以幫我們把 UI 渲染到 canvas 上,或者是有一個叫 react-app 可以幫我們把它轉(zhuǎn)換成原生的 App(實際上這玩意叫 ReactNative)。

7.組件的 render 方法

React.js 中一切皆組件,用 React.js 寫的其實就是 React.js 組件。

條件返回 JSX 的方式在 React.js 中很常見,組件的呈現(xiàn)方式隨著數(shù)據(jù)的變化而不一樣,你可以利用 JSX 這種靈活的方式隨時組合構(gòu)建不同的頁面結(jié)構(gòu)。

8.組件的組合、嵌套和組件樹

class Title extends Component {
  render () {
    return (
      <h1>React 小書</h1>
    )
  }
}

class Header extends Component {
  render () {
    return (
      <div>
        <Title />
      </div>
    )
  }
}

我們可以直接在 Header 標(biāo)簽里面直接使用 Title 標(biāo)簽。就像是一個普通的標(biāo)簽一樣。

這樣可復(fù)用性非常強,我們可以把組件的內(nèi)容封裝好,然后靈活在使用在任何組件內(nèi)。

自定義的組件都必須要用大寫字母開頭,普通的 HTML 標(biāo)簽都用小寫字母開頭。

組件可以和組件組合在一起,組件內(nèi)部可以使用別的組件。就像普通的 HTML 標(biāo)簽一樣使用就可以。這樣的組合嵌套,最后構(gòu)成一個所謂的組件樹.

9.事件監(jiān)聽

在 React.js 里面監(jiān)聽事件是很容易的事情,你只需要給需要監(jiān)聽事件的元素加上屬性類似于 onClick、onKeyDown 這樣的屬性。

class Title extends Component {
  handleClickOnTitle () {
  console.log('Click on title.')
}

render () {
  return (
    <h1 onClick={this.handleClickOnTitle}>React 小書</h1>
    )
  }
}

在 React.js 不需要手動調(diào)用瀏覽器原生的addEventListener進行事件監(jiān)聽。React.js 幫我們封裝好了一系列的on*的屬性,而且你不需要考慮不同瀏覽器兼容性的問題,React.js 都幫我們封裝好這些細節(jié)了。

React.js 封裝了不同類型的事件,具體可見官方文檔:合成事件。另外要注意的是,這些事件屬性名都必須要用駝峰命名法。

沒有經(jīng)過特殊處理的話,這些on*的事件監(jiān)聽只能用在普通的 HTML 的標(biāo)簽上,而不能用在組件標(biāo)簽上。也就是說,<Header onClick={…} />這樣的寫法不會有什么效果的。這一點要注意,但是有辦法可以做到這樣的綁定,以后我們會提及?,F(xiàn)在只要記住一點就可以了:這些on*的事件監(jiān)聽只能用在普通的 HTML 的標(biāo)簽上,而不能用在組件標(biāo)簽上。

event 對象

和普通瀏覽器一樣,事件監(jiān)聽函數(shù)會被自動傳入一個 event 對象,這個對象和普通的瀏覽器 event 對象所包含的方法和屬性都基本一致。

不同的是 React.js 中的 event 對象并不是瀏覽器提供的,而是它自己內(nèi)部所構(gòu)建的。

class Title extends Component {
  handleClickOnTitle (e) {
    console.log(e.target.innerHTML)
  }

  render () {
    return (
      <h1 onClick={this.handleClickOnTitle}>React 小書</h1>
    )
  }
}

每次點擊的時候就會打印“React 小書”。

關(guān)于事件中的 this

一般在某個類的實例方法里面的 this 指的是這個實例本身。

如果你想在事件函數(shù)當(dāng)中使用當(dāng)前的實例,你需要手動地將實例方法 bind 到當(dāng)前實例上再傳入給 React.js。

class Title extends Component {
  handleClickOnTitle (e) {
    console.log(this)
  }

  render () {
    return (
      <h1 onClick={this.handleClickOnTitle.bind(this)}>React 小書</h1>
    )
  }
}

bind會把實例方法綁定到當(dāng)前實例上,然后我們再把綁定后的函數(shù)傳給 React.js 的onClick事件監(jiān)聽。

總結(jié):
- 為 React 的組件添加事件監(jiān)聽是很簡單的事情,你只需要使用 React.js 提供了一系列的 on* 方法即可。
- React.js 會給每個事件監(jiān)聽傳入一個 event 對象,這個對象提供的功能和瀏覽器提供的功能一致,而且它是兼容所有瀏覽器的。
- React.js 的事件監(jiān)聽方法需要手動 bind 到當(dāng)前實例,這種模式在 React.js 中非常常用。

10.組件的 state 和 setState

一個組件的顯示形態(tài)是可以由它數(shù)據(jù)狀態(tài)和配置參數(shù)決定的。React.js 的state就是用來存儲這種可變化的狀態(tài)的。

setState方法由父類 Component 所提供。當(dāng)我們調(diào)用這個函數(shù)的時候,React.js 會更新組件的狀態(tài) state ,并且重新調(diào)用 render 方法,然后再把 render 方法所渲染的最新的內(nèi)容顯示到頁面上。

setState 接受函數(shù)參數(shù)

這里還有要注意的是,當(dāng)你調(diào)用 setState 的時候,React.js 并不會馬上修改 state。而是把這個對象放到一個更新隊列里面,稍后才會從隊列當(dāng)中把新的狀態(tài)提取出來合并到 state 當(dāng)中,然后再觸發(fā)組件更新。

React.js 會把上一個 setState 的結(jié)果傳入一個函數(shù),你就可以使用該結(jié)果進行運算、操作,然后返回一個對象作為更新 state 的對象。

...
  handleClickOnLikeButton () {
    this.setState((prevState) => {
      return { count: 0 }
    })
    this.setState((prevState) => {
      return { count: prevState.count + 1 } // 上一個 setState 的返回是 count 為 0,當(dāng)前返回 1
    })
    this.setState((prevState) => {
      return { count: prevState.count + 2 } // 上一個 setState 的返回是 count 為 1,當(dāng)前返回 3
    })
    // 最后的結(jié)果是 this.state.count 為 3
  }
...

這樣就可以達到上述的利用上一次 setState 結(jié)果進行運算的效果。

setState 合并

上面我們進行了三次 setState,但是實際上組件只會重新渲染一次,而不是三次;這是因為在 React.js 內(nèi)部會把 JavaScript 事件循環(huán)中的消息隊列的同一個消息中的 setState 都進行合并以后再重新渲染組件。

你只需要記住的是:在使用 React.js 的時候,并不需要擔(dān)心多次進行 setState 會帶來性能問題

11.配置組件的 props

如果想讓組件能適應(yīng)不同場景下的需求,我們就要讓組件具有一定的“可配置”性。

React.js 的props就可以幫助我們達到這個效果。每個組件都可以接受一個 props 參數(shù),它是一個對象,包含了所有你對這個組件的配置。

render () {
  const likedText = this.props.likedText || '取消'
  const unlikedText = this.props.unlikedText || '點贊'
  return (
    <button onClick={this.handleClickOnLikeButton.bind(this)}>
        {this.state.isLiked ? likedText : unlikedText} 
    </button>
  )
}

如何把 props 傳進去呢?在使用一個組件的時候,可以把參數(shù)放在標(biāo)簽的屬性當(dāng)中,所有的屬性都會作為 props 對象的鍵值

class Index extends Component {
  render () {
    return (
      <div>
        <LikeButton likedText='已贊' unlikedText='贊' />
      </div>
    )
  }
}

JSX 的表達式插入可以在標(biāo)簽屬性上使用。所以其實可以把任何類型的數(shù)據(jù)作為組件的參數(shù),包括字符串、數(shù)字、對象、數(shù)組、甚至是函數(shù)等等。

<LikeButton wordings={{likedText: '已贊', unlikedText: '贊'}} />

現(xiàn)在我們把 likedText 和 unlikedText 這兩個參數(shù)封裝到一個叫 wordings 的對象參數(shù)內(nèi),然后傳入點贊組件中。

render () {
  const wordings = this.props.wordings || {
    likedText: '取消',
    unlikedText: '點贊'
  }
  return (
    <button onClick={this.handleClickOnLikeButton.bind(this)}>
        {this.state.isLiked ? wordings.likedText : wordings.unlikedText} 
    </button>
  )
}

一個組件的行為、顯示形態(tài)都可以用 props 來控制,可以達到很好的可配置性。

默認配置 defaultProps

上面的組件默認配置我們是通過||操作符來實現(xiàn)。這種需要默認配置的情況在 React.js 中非常常見,所以 React.js 也提供了一種方式defaultProps,可以方便的做到默認配置。

class LikeButton extends Component {
  static defaultProps = {
    likedText: '取消',
    unlikedText: '點贊'
  }

  constructor () {
    super()
    this.state = { isLiked: false }
  }

  handleClickOnLikeButton () {
    this.setState({
      isLiked: !this.state.isLiked
    })
  }

  render () {
    return (
      <button onClick={this.handleClickOnLikeButton.bind(this)}>
        {this.state.isLiked
          ? this.props.likedText
          : this.props.unlikedText} 
      </button>
    )
  }
}
props 不可變

props 一旦傳入進來就不能改變。

你不能改變一個組件被渲染的時候傳進來的 props。React.js 希望一個組件在輸入確定的 props 的時候,能夠輸出確定的 UI 顯示形態(tài)。

但這并不意味著由 props 決定的顯示形態(tài)不能被修改。組件的使用者可以主動地通過重新渲染的方式把新的 props 傳入組件當(dāng)中,這樣這個組件中由 props 決定的顯示形態(tài)也會得到相應(yīng)的改變。

總結(jié):
- 為了使得組件的可定制性更強,在使用組件的時候,可以在標(biāo)簽上加屬性來傳入配置參數(shù)。
- 組件可以在內(nèi)部通過 this.props 獲取到配置參數(shù),組件可以根據(jù) props 的不同來確定自己的顯示形態(tài),達到可配置的效果。
- 可以通過給組件添加類屬性 defaultProps 來配置默認參數(shù)。
- props 一旦傳入,你就不可以在組件內(nèi)部對它進行修改。但是你可以通過父組件主動重新渲染的方式來傳入新的 props,從而達到更新的效果。

12.state vs props

state 的主要作用是用于組件保存、控制、修改自己的可變狀態(tài)。state 在組件內(nèi)部初始化,可以被組件自身修改,而外部不能訪問也不能修改。你可以認為 state 是一個局部的、只能被組件自身控制的數(shù)據(jù)源。state 中狀態(tài)可以通過 this.setState 方法進行更新,setState 會導(dǎo)致組件的重新渲染。

props 的主要作用是讓使用該組件的父組件可以傳入?yún)?shù)來配置該組件。它是外部傳進來的配置參數(shù),組件內(nèi)部無法控制也無法修改。除非外部組件主動傳入新的 props,否則組件的 props 永遠保持不變。

state 和 props 有著千絲萬縷的關(guān)系。它們都可以決定組件的行為和顯示形態(tài)。但是它們的職責(zé)其實非常明晰分明:state 是讓組件控制自己的狀態(tài),props 是讓外部對組件自己進行配置。

沒有 state 的組件叫無狀態(tài)組件(stateless component),設(shè)置了 state 的叫做有狀態(tài)組件(stateful component)。因為狀態(tài)會帶來管理的復(fù)雜性,我們盡量多地寫無狀態(tài)組件,盡量少地寫有狀態(tài)的組件。

React.js 非常鼓勵無狀態(tài)組件,在 0.14 版本引入了函數(shù)式組件 —— 一種定義不能使用 state 組件,你可以理解函數(shù)式組件就是一種只能接受 props 和提供 render 方法的類組件。

13.渲染列表數(shù)據(jù)

渲染存放 JSX 元素的數(shù)組

JSX 的表達式插入 {} 里面可以放任何數(shù)據(jù),如果我們往 {} 里面放一個存放 JSX 元素的數(shù)組會怎么樣?

class Index extends Component {
  render () {
    return (
      <div>
        {[
          <span>React.js </span>,
          <span>is </span>,
          <span>good</span>
        ]}
      </div>
    )
  }
}

到瀏覽器中,你在頁面上會看到:“React.js is good”。
React.js 把插入表達式數(shù)組里面的每一個 JSX 元素一個個羅列下來,渲染到頁面上。如果你往 {} 放一個數(shù)組,React.js 會幫你把數(shù)組里面一個個元素羅列并且渲染出來。

使用 map 渲染列表數(shù)據(jù)
const users = [
  { username: 'Jerry', age: 21, gender: 'male' },
  { username: 'Tomy', age: 22, gender: 'male' },
  { username: 'Lily', age: 19, gender: 'female' },
  { username: 'Lucy', age: 20, gender: 'female' }
]

class Index extends Component {
  render () {
    const usersElements = [] // 保存每個用戶渲染以后 JSX 的數(shù)組
    for (let user of users) {
      usersElements.push( // 循環(huán)每個用戶,構(gòu)建 JSX,push 到數(shù)組中
        <div>
          <div>姓名:{user.username}</div>
          <div>年齡:{user.age}</div>
          <div>性別:{user.gender}</div>
          <hr />
        </div>
      )
    }

    return (
      <div>{usersElements}</div>
    )
  }
}

這里用了一個新的數(shù)組 usersElements,然后循環(huán) users 數(shù)組,為每個 user 構(gòu)建一個 JSX 結(jié)構(gòu),然后 push 到 usersElements 中。然后直接用表達式插入,把這個 userElements 插到 return 的 JSX 當(dāng)中。

但我們一般不會手動寫循環(huán)來構(gòu)建列表的 JSX 結(jié)構(gòu),可以直接用 ES6 自帶的map

class Index extends Component {
  render () {
    return (
      <div>
        {users.map((user) => {
          return (
            <div>
              <div>姓名:{user.username}</div>
              <div>年齡:{user.age}</div>
              <div>性別:{user.gender}</div>
              <hr />
            </div>
          )
        })}
      </div>
    )
  }
}

這樣的模式在 JavaScript 中非常常見,一般來說,在 React.js 處理列表就是用 map 來處理、渲染的。

對于用表達式套數(shù)組羅列到頁面上的元素,都要為每個元素加上 key 屬性,這個 key 必須是每個元素唯一的標(biāo)識。

14.實戰(zhàn)分析:評論功能

我們遵循一個原則:如果一個文件導(dǎo)出的是一個類,那么這個文件名就用大寫開頭。四個組件類文件導(dǎo)出都是類,所以都是大寫字母開頭。

類似于<input /><select />、<textarea> 這些元素的 value 值被 React.js 所控制、渲染的組件,在 React.js 當(dāng)中被稱為受控組件(Controlled Component)。對于用戶可輸入的控件,一般都可以讓它們成為受控組件,這是 React.js 所推崇的做法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容