React入門教程(3)React的組件及組件的生命周期

React組件

組件可以將UI切分成一些獨立的、可復用的部件,這樣你就只需專注于構建每一個單獨的部件。

組件從概念上看就像是函數,它可以接收任意的輸入值(稱之為“props”),并返回一個需要在頁面上展示的React元素。

函數定義/類定義組件

定義一個組件最簡單的方式是使用JavaScript函數:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

該函數是一個有效的React組件,它接收一個單一的“props”對象并返回了一個React元素。我們之所以稱這種類型的組件為函數定義組件,是因為從字面上來看,它就是一個JavaScript函數。

你也可以使用 ES6 class 來定義一個組件:

class Welcome extends React.Component {
  render() {
    return <h1>Hello, {this.props.name}</h1>;
  }
}

上面兩個組件在React中是相同的。

組件渲染

在前面,我們遇到的React元素都只是DOM標簽:

const element = <div />;

然而,React元素也可以是用戶自定義的組件:

const element = <Welcome name="Sara" />;

當React遇到的元素是用戶自定義的組件,它會將JSX屬性作為單個對象傳遞給該組件,這個對象稱之為“props”。

例如,這段代碼會在頁面上渲染出"Hello,Sara":

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

// ...
class App extends Component {
  render() {
    return (
      <div className="App">
        <Welcome name="Sara" />
      </div>
    );
  }
}
// ...

我們來回顧一下在這個例子中發生了什么:

  1. 我們對<Welcome name="Sara" />元素調用了ReactDOM.render()方法。
  2. React將{name: 'Sara'}作為props傳入并調用Welcome組件。
  3. Welcome組件將<h1>Hello, Sara</h1>元素作為結果返回。
  4. 將DOM更新為<h1>Hello, Sara</h1>

警告:

組件名稱必須以大寫字母開頭。

例如,<div /> 表示一個DOM標簽,但 <Welcome /> 表示一個組件,并且在使用該組件時你必須定義或引入它。

組合組件

組件可以在它的輸出中引用其它組件,這就可以讓我們用同一組件來抽象出任意層次的細節。在React應用中,按鈕、表單、對話框、整個屏幕的內容等,這些通常都被表示為組件。

例如,我們可以創建一個App組件,用來多次渲染Welcome組件:

function Welcome(props) {
  return <h1>Hello, {props.name}</h1>;
}

function App() {
  return (
    <div>
      <Welcome name="Sara" />
      <Welcome name="Cahal" />
      <Welcome name="Edite" />
    </div>
  );
}

通常,一個新的React應用程序的頂部是一個App組件。但是,如果要將React集成到現有應用程序中,則可以從下而上使用像Button這樣的小組件作為開始,并逐漸運用到視圖層的頂部。

警告:

組件的返回值只能有一個根元素。這也是我們要用一個<div>來包裹所有<Welcome />元素的原因。

提取組件

你可以將組件切分為更小的組件,這沒什么好擔心的。

例如,來看看這個Comment組件:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <img className="Avatar"
          src={props.author.avatarUrl}
          alt={props.author.name}
        />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

這個組件接收author(對象)、text(字符串)、以及date(Date對象)作為props,用來描述一個社交媒體網站上的評論。

這個組件由于嵌套,變得難以被修改,可復用的部分也難以被復用。所以讓我們從這個組件中提取出一些小組件。

首先,我們來提取Avatar組件:

function Avatar(props) {
  return (
    <img className="Avatar"
      src={props.user.avatarUrl}
      alt={props.user.name}
    />
  );
}

Avatar作為Comment的內部組件,不需要知道是否被渲染。因此我們將author改為一個更通用的名字user

我們建議從組件自身的角度來命名props,而不是根據使用組件的上下文命名。

現在我們可以對Comment組件做一些小小的調整:

function Comment(props) {
  return (
    <div className="Comment">
      <div className="UserInfo">
        <Avatar user={props.author} />
        <div className="UserInfo-name">
          {props.author.name}
        </div>
      </div>
      <div className="Comment-text">
        {props.text}
      </div>
      <div className="Comment-date">
        {formatDate(props.date)}
      </div>
    </div>
  );
}

提取組件一開始看起來像是一項單調乏味的工作,但是在大型應用中,構建可復用的組件完全是值得的。當你的UI中有一部分重復使用了好幾次(比如,ButtonPanelAvatar),或者其自身就足夠復雜(比如,AppFeedStoryComment),類似這些都是抽象成一個可復用組件的絕佳選擇,這也是一個比較好的做法。

Props的只讀性

無論是使用函數或是類來聲明一個組件,它決不能修改它自己的props。來看這個sum函數:

function sum(a, b) {
  return a + b;
}

類似于上面的這種函數稱為“純函數”,它沒有改變它自己的輸入值,當傳入的值相同時,總是會返回相同的結果。

與之相對的是非純函數,它會改變它自身的輸入值:

function withdraw(account, amount) {
  account.total -= amount;
}

React是非常靈活的,但它也有一個嚴格的規則:

所有的React組件必須像純函數那樣使用它們的props。

當然,應用的界面是隨時間動態變化的,后面會介紹一種稱為“state”的新概念,State可以在不違反上述規則的情況下,根據用戶操作、網絡響應、或者其他狀態變化,使組件動態的響應并改變組件的輸出。

組件的生命周期

React組件的生命周期圖解

React 生命周期圖解

組件會隨著組件的props和state改變而發生變化,它的DOM也會有相應的變化。一個組件就是一個狀態機:對于特定的輸入,它總會返回一致的輸出。

React組件提供了生命周期的鉤子函數去響應組件不同時刻的狀態,組件的生命周期如下:

  • 實例化階段
  • 存在期階段
  • 銷毀期階段

實例化階段

首次調用組件時,實例化階段有以下方法會被調用(注意順序,從上到下先后執行):

getDefaultProps

這個方法是用來設置組件默認的props,組件生命周期只會調用一次。但是只適合React.createClass直接創建的組件,使用ES6/ES7創建的這個方法不可使用,ES6/ES7可以使用下面方式:

//es7
class Component {
  static defaultProps = {}
}

getInitialState

設置state初始值,在這個方法中你已經可以訪問到this.props。getDefaultProps只適合React.createClass使用。使用ES6初始化state方法如下:

class Component extends React.Component {
  constructor(props){
    super(props);
    this.state = {
      render: true,
    }
  }
}
// 或者這樣, es6 stage3

class Component extends React.Component{
  state = {
    render: true
  }
  render(){return false;}
}

componentWillMount

此方法會在組件首次渲染之前調用,這個是在render方法調用前可修改state的最后一次機會。這個方法很少用到。

render

這個方法以后大家都應該會很熟悉,JSX通過這里,解析成對應的虛擬DOM,渲染成最終效果。格式大致如下:

class Component extends React.Component{
  render(){
    return (
       <div></div>
    )
  }
}

componentDidMount

這個方法在首次真實的DOM渲染后調用(僅此一次)當我們需要訪問真實的DOM時,這個方法就經常用到。如何訪問真實的DOM這里就不想說了。當我們需要請求外部接口數據,一般都在這里處理。

存在期

實例化后,當props或者state發生變化時,下面方法依次被調用:

componentWillReceiveProps

沒當我們通過父組件更新子組件props時(這個也是唯一途徑),這個方法就會被調用。

componentWillReceiveProps(nextProps){}

shouldComponentUpdate

字面意思,是否應該更新組件,默認返回true。當返回false時,后期函數就不會調用,組件不會在次渲染。

shouldComponentUpdate(nextProps,nextState){}

componentWillUpdate

字面意思組件將會更新,props和state改變后必調用。

render

跟實例化時的render一樣,不多說

componentDidUpdate

這個方法在更新真實的DOM成功后調用,當我們需要訪問真實的DOM時,這個方法就也經常用到。

銷毀期

銷毀階段,只有一個函數被調用:

componentWillUnmount

沒當組件使用完成,這個組件就必須從DOM中銷毀,此時該方法就會被調用。當我們在組件中使用了setInterval,那我們就需要在這個方法中調用clearTimeout。

參考:React組件生命周期

參考

  1. 官網文檔
  2. 老馬React視頻地址: https://ke.qq.com/course/379234?tuin=1eb4a0a4
  3. AICODER官網地址:https://www.aicoder.com/
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 作為一個合格的開發者,不要只滿足于編寫了可以運行的代碼。而要了解代碼背后的工作原理;不要只滿足于自己的程序...
    六個周閱讀 8,526評論 1 33
  • HTML模版 之后出現的React代碼嵌套入模版中。 1. Hello world 這段代碼將一個一級標題插入到指...
    ryanho84閱讀 6,308評論 0 9
  • $ 前言 ? 最近在考慮框架轉型,鑒于作為一名JSer,要時時刻刻保持對新技術和流行技術的敏感性,而 React、...
    果汁涼茶丶閱讀 22,066評論 5 32
  • 3. JSX JSX是對JavaScript語言的一個擴展語法, 用于生產React“元素”,建議在描述UI的時候...
    pixels閱讀 2,891評論 0 24
  • 安裝: 概述 React起源于FaceBook的內部項目,因為該公司對市場上所有的JavaScript MVC框架...
    姒沝無痕閱讀 738評論 0 0