前端學(xué)習(xí)筆記四十-移動(dòng)APP(4)React組件的生命周期

一、組件的生命周期

1.概念:

在組件創(chuàng)建、到加載到頁面上運(yùn)行、以及組件被銷毀的過程中,總是伴隨著各種各樣的事件,這些在組件特定時(shí)期,觸發(fā)的事件,統(tǒng)稱為 組件的生命周期;

2.組件生命周期分為三部分:
  • 組件創(chuàng)建階段:組件創(chuàng)建階段的生命周期函數(shù),有一個(gè)顯著的特點(diǎn):創(chuàng)建階段的生命周期函數(shù),在組件的一輩子中,只執(zhí)行一次;

componentWillMount: 組件將要被掛載,此時(shí)還沒有開始渲染虛擬DOM,此時(shí)props和state中的數(shù)據(jù)都能獲取到。
render:第一次開始渲染真正的虛擬DOM,當(dāng)render執(zhí)行完,內(nèi)存中就有了完整的虛擬DOM了
componentDidMount: 組件完成了掛載(掛載即把虛擬DOM樹顯示到頁面上),此時(shí),組件已經(jīng)顯示到了頁面上,當(dāng)這個(gè)方法執(zhí)行完,組件就進(jìn)入了 運(yùn)行中 的狀態(tài)

  • 組件運(yùn)行階段:也有一個(gè)顯著的特點(diǎn),根據(jù)組件的state和props的改變,有選擇性的觸發(fā)0次或多次;

componentWillReceiveProps: 組件將要接收新屬性,此時(shí),只要這個(gè)方法被觸發(fā),就證明父組件為當(dāng)前子組件傳遞了新的屬性值;
shouldComponentUpdate: 組件是否需要被更新,此時(shí),組件尚未被更新,但是,state 和 props 肯定是最新的
componentWillUpdate: 組件將要被更新,此時(shí),尚未開始更新,內(nèi)存中的虛擬DOM樹還是舊的
render: 此時(shí),又要重新根據(jù)最新的 state 和 props 重新渲染一棵內(nèi)存中的虛擬DOM樹,當(dāng) render 調(diào)用完畢,內(nèi)存中的舊DOM樹,已經(jīng)被新DOM樹替換了!此時(shí)頁面還是舊的
componentDidUpdate: 此時(shí),頁面又被重新渲染了,state 和 虛擬DOM 和 頁面已經(jīng)完全保持同步

  • 組件銷毀階段:也有一個(gè)顯著的特點(diǎn),一輩子只執(zhí)行一次;

componentWillUnmount: 組件將要被卸載,此時(shí)組件還可以正常使用;

vue中的生命周期圖
React Native 中組件的生命周期

React中組件的生命周期 - 詳解
3.defaultProps

在組件創(chuàng)建之前,會先初始化默認(rèn)的props屬性,這是全局調(diào)用一次,嚴(yán)格地來說,這不是組件的生命周期的一部分。在組件被創(chuàng)建并加載候,首先調(diào)用 constructor 構(gòu)造器中的 this.state = {},來初始化組件的狀態(tài)。

React生命周期的回調(diào)函數(shù)總結(jié)成表格如下:


React生命周期表格
4.組件生命周期的執(zhí)行順序:
  • Mounting:
  • constructor()
  • componentWillMount()
  • render()
  • componentDidMount()
  • Updating:
  • componentWillReceiveProps(nextProps)
  • shouldComponentUpdate(nextProps, nextState)
  • componentWillUpdate(nextProps, nextState)
  • render()
  • componentDidUpdate(prevProps, prevState)
  • Unmounting:
  • componentWillUnmount()

二、通過Counter計(jì)數(shù)器的小案例 - 了解生命周期函數(shù)

  1. 給組件設(shè)置默認(rèn)屬性:
  // 在封裝一個(gè)組件的時(shí)候,組件內(nèi)部,肯定有一些數(shù)據(jù)是必須的,哪怕用戶沒有傳遞一些相關(guān)的啟動(dòng)參數(shù),這時(shí)候,組件內(nèi)部 ,盡量 給自己提供一個(gè)默認(rèn)值;
  // 在 React 中,使用靜態(tài)的 defaultProps 屬性,來設(shè)置 組件的 默認(rèn)屬性值;
  static defaultProps = {
    initcount: 0 // 如果外界沒有傳遞 initcount,那么,自己初始化一個(gè) 數(shù)值,為0
  }
  1. 給屬性進(jìn)行類型校驗(yàn),需要先運(yùn)行cnpm i prop-types --save
  // 注意: prop-types 包中職能跟單一,只提供了 一些常見的 數(shù)據(jù)類型,用于做類型校驗(yàn)
  import ReactTypes from 'prop-types'
  // 這是創(chuàng)建一個(gè) 靜態(tài)的 propTypes 對象,在這個(gè)對象中,可以把 外界傳遞過來的屬性,做類型校驗(yàn);
  // 注意: 如果要為 傳遞過來的屬性做類型校驗(yàn),必須安裝 React 提供的 第三方包,叫做 prop-types ;
  // prop-types 大概在 v.15.* 之前,并沒有單獨(dú)抽離出來,那時(shí)候,還和 react 包 在一起;后來, 從 v.15.* 之后,官方把類型校驗(yàn)的 模塊,單獨(dú)抽離為 一個(gè)包,就叫做 prop-types
  static propTypes = {
    initcount: ReactTypes.number // 使用 prop-types 包,來定義 initcount 為 number 類型
  }
組件初始化時(shí)生命周期事件總結(jié)
  1. componentWillMount:等同于Vue中的created生命周期函數(shù)。 此時(shí),props和state中的數(shù)據(jù)都能獲取到。但無法獲取到頁面上的任何元素,因?yàn)樘摂MDOM和頁面都還沒有開始渲染。【在這個(gè)階段中,不能去操作頁面上的DOM元素】

  2. render:即將要開始渲染內(nèi)存中的虛擬DOM,當(dāng) return 執(zhí)行完畢后,虛擬DOM創(chuàng)建好了,但是只是存在于內(nèi)存中,還沒有掛載到真正的頁面中。

  3. componentDidMount:相當(dāng)于 Vue 中的 mounted 函數(shù)。當(dāng)組件掛載到頁面上之后,就是進(jìn)入這個(gè)生命周期函數(shù)了,頁面上已經(jīng)有可見的DOM元素了。組件就就進(jìn)入到了運(yùn)行中的狀態(tài)。如果我們想操作DOM元素,最早只能在 componentDidMount 中進(jìn)行;

通過原生的方式獲取元素并綁定事件
componentDidMount() {
    document.getElementById('btn').onclick = () => {
      // console.log(this);
      // this.props.initcount++
      this.setState({
        count: this.state.count + 1
      })
    } 
  }
React中使用ref屬性獲取DOM元素引用

和 Vue 中差不多,vue 為頁面上的元素提供了 ref 的屬性,如果想要獲取元素引用,則需要使用this.$refs.引用名稱

在 React 中,也有 ref, 如果要獲取元素的引用this.refs.引用名稱

<h3 id="myh3" ref="h3">當(dāng)前的數(shù)量是:{this.state.count}</h3>

componentWillUpdate() {
    console.log(document.getElementById('myh3').innerHTML)
    console.log(this.refs.h3.innerHTML);
  }
使用React中的事件,綁定count自增
<input type="button" value="+1" id="btn" onClick={this.increment} />

increment = () => {
    this.setState({
      count: this.state.count + 1
    })
  }
組件運(yùn)行中事件的總結(jié)對比
  1. componentWillReceiveProps:當(dāng)組件第一次被渲染到頁面上的時(shí)候,不會觸發(fā)這個(gè)函數(shù);只有當(dāng)父級通過某些事件,重新修改了傳遞給該組件的 props 數(shù)據(jù)之后,才會觸發(fā)這個(gè)函數(shù)。這時(shí)如果使用 this.props 來獲取的屬性值不是最新的,是上一次的舊屬性值;最新的屬性值需要通過參數(shù)列表來獲取。nextProps參數(shù)表示組件將要接收外界傳遞過來的新的 props 屬性值。

  2. shouldComponentUpdate:必須返回一個(gè)布爾值,如果返回的值是 false,則不會繼續(xù)執(zhí)行后續(xù)的生命周期函數(shù),而是直接退回到了運(yùn)行中的狀態(tài),此時(shí)由于后續(xù)的 render 函數(shù)并沒有被調(diào)用,因此頁面不會被更新,但是, 組件的 state 狀態(tài)卻被修改了;

  3. componentWillUpdate:組件將要更新但尚未更新,內(nèi)存中的虛擬DOM是舊的,頁面上的DOM 元素也是舊的。應(yīng)該慎重操作,因?yàn)槟憧赡懿僮鞯氖桥fDOM。

  4. render:在組件運(yùn)行階段中,每當(dāng)調(diào)用 render 函數(shù)的時(shí)候,頁面上的 DOM元素,還是之前舊的。在render函數(shù)中,不能調(diào)用setState()方法,因?yàn)闀萑胨姥h(huán)(數(shù)據(jù)被修改又要重新更新組件,又會進(jìn)入render函數(shù))。

  5. componentDidUpdate:組件完成了更新,此時(shí)state 中的數(shù)據(jù)、虛擬DOM、頁面上的DOM,都是最新的,此時(shí),你可以放心大膽的去操作頁面了

三、綁定this并傳參的三種方式

  1. 在事件中綁定this并傳參:每次調(diào)用都要bind綁定this
    //React中駝峰方式綁定事件的js中this指向組件實(shí)例
    <input type="button" value="在事件中綁定this并傳參" onClick={this.handleMsg1.bind(this, '??', '??')} />

    // 在事件中綁定this并傳參
    handleMsg1(arg1, arg2) {
        console.log(this);
        // 此時(shí)this是個(gè)null
        this.setState({
            msg: '在事件中綁定this并傳參:' + arg1 + arg2
        });
    }
  1. 在構(gòu)造函數(shù)(constructor中)中綁定this并傳參:調(diào)用的時(shí)候不用再手動(dòng)綁定
    // 修改構(gòu)造函數(shù)中的代碼:
    //當(dāng)為一個(gè)函數(shù)調(diào)用 bind 改變了this指向后,bind 函數(shù)調(diào)用后有一個(gè)返回值,就是被改變this指向后的函數(shù)的引用;bind 不會修改 原函數(shù)的 this 指向,因此需要把返回值重新賦值給原函數(shù)
    this.handleMsg2 = this.handleMsg2.bind(this, '??', '??');

    <input type="button" value="在構(gòu)造函數(shù)中綁定this并傳參" onClick={this.handleMsg2} />

    // 在構(gòu)造函數(shù)中綁定this并傳參
    handleMsg2(arg1, arg2) {
        this.setState({
            msg: '在構(gòu)造函數(shù)中綁定this并傳參:' + arg1 + arg2
        });
    }
  1. 用箭頭函數(shù)綁定this并傳參:
    <input type="button" value="用箭頭函數(shù)綁定this并傳參" onClick={() => { this.handleMsg3('??', '??') }} />

    // 用箭頭函數(shù)綁定this并傳參
    handleMsg3(arg1, arg2) {
        this.setState({
            msg: '用箭頭函數(shù)綁定this并傳參:' + arg1 + arg2
        });
    }

注意,在React中DOM綁定事件不能賦值為一個(gè)函數(shù)的調(diào)用,如onClick={this.handleMsg('arg')},因?yàn)镽eact執(zhí)行到此會直接執(zhí)行這個(gè)調(diào)用,只能賦值為一個(gè)函數(shù)

四、綁定文本框與state中的值

  1. 在Vue.js中,默認(rèn)可以通過v-model指令,將表單控件和我們的data上面的屬性進(jìn)行雙向數(shù)據(jù)綁定,數(shù)據(jù)變化和頁面之間的變化是同步的!
  2. 在React.js中,默認(rèn)沒有提供雙向數(shù)據(jù)綁定這一功能,默認(rèn)只能把state之上的數(shù)據(jù)同步到界面的控件上,但是不能默認(rèn)實(shí)現(xiàn)把界面上數(shù)據(jù)的改變,同步到state之上,需要程序員手動(dòng)調(diào)用相關(guān)的事件來進(jìn)行逆向的數(shù)據(jù)傳輸!
  3. 綁定文本框和state的值:
    {/*只要將value屬性,和state上的狀態(tài)進(jìn)行綁定,那么這個(gè)表單元素就變成了受控表單元素,這時(shí)如果沒有調(diào)用相關(guān)的事件,是無法手動(dòng)修改表單元素中的值的*/}
    <input style={{ width: '100%' }} ref="txt" type="text" value={this.state.msg} onChange={this.handleTextChange} />

    // 這是文本框內(nèi)容改變時(shí)候的處理函數(shù)
    handleTextChange = (e) => {
        this.setState({
            //msg: e.target.value
            msg: this.refs.txt.value
        });
    }
  1. 注意setState的一個(gè)問題
// setState在保存的時(shí)候是異步地進(jìn)行保存的,所以如果想要獲取最新的,剛剛保存的那個(gè)狀態(tài),需要通過回掉函數(shù)的形式去獲取最新state
this.setState({
    msg: this.refs.txt.value
    // msg: e.target.value
}, function () {
    // 獲取最新的state狀態(tài)值
    console.log(this.state.msg);
});

五、發(fā)表評論案例

CMTList組件

import React from 'react'
import CMTItem from './CmtItem.jsx'
import CMTBox from './CmtBox.jsx'

// 評論列表組件
export default class CMTList extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      list: [
        { user: 'zs', content: '123' },
        { user: 'ls', content: 'qqq' },
        { user: 'xiaohong', content: 'www' }
      ]
    }
  }

  // 在組件尚未渲染的時(shí)候,就立即 獲取數(shù)據(jù)
  componentWillMount() {
    this.loadCmts()
  }

  render() {
    return <div>
      <h1>這是評論列表組件</h1>

      {/* 發(fā)表評論的組件 */}
      {/* 相對于 Vue 中,把 父組件傳遞給子組件的 普通屬性 和 方法屬性,區(qū)別對待, 普通屬性用 props 接收, 方法 使用 this.$emit('方法名') */}
      {/* react 中,只要是傳遞給 子組件的數(shù)據(jù),不管是 普通的類型,還是方法,都可以使用 this.props 來調(diào)用 */}
      <CMTBox reload={this.loadCmts}></CMTBox>

      <hr />

      {/* 循環(huán)渲染一些評論內(nèi)容組件 */}
      {this.state.list.map((item, i) => {
        return <CMTItem key={i} {...item}></CMTItem>
      })}
    </div>
  }

  // 從本地存儲中加載 評論列表
  loadCmts = () => {
    var list = JSON.parse(localStorage.getItem('cmts') || '[]')
    this.setState({
      list
    })
  }
}

CMTItem組件

import React from 'react'

// 評論列表項(xiàng)組件
export default class CMTItem extends React.Component {

  render() {
    return <div style={{ border: '1px solid #ccc', margin: '10px 0' }}>
      <h3>評論人:{this.props.user}</h3>
      <h5>評論內(nèi)容:{this.props.content}</h5>
    </div>
  }
}

CMTBox組件

import React from 'react'

// 評論列表框組件
export default class CMTBox extends React.Component {

  render() {
    return <div>
      <label>評論人:</label><br />
      <input type="text" ref="user" /><br />
      <label>評論內(nèi)容:</label><br />
      <textarea cols="30" rows="4" ref="content"></textarea><br />

      <input type="button" value="發(fā)表評論" onClick={this.postComment} />
    </div>
  }

  postComment = () => {
    // 1. 獲取到評論人和評論內(nèi)容
    // 2. 從 本地存儲中,先獲取之前的評論數(shù)組
    // 3. 把 最新的這條評論,unshift 進(jìn)去
    // 4. 在把最新的評論數(shù)組,保存到 本地存儲中
    var cmtInfo = { user: this.refs.user.value, content: this.refs.content.value }
    var list = JSON.parse(localStorage.getItem('cmts') || '[]')
    list.unshift(cmtInfo)
    localStorage.setItem('cmts', JSON.stringify(list))

    this.refs.user.value = this.refs.content.value = ''

    this.props.reload()
  }
}

六、擴(kuò)展

context特性

父組件

  // 1. 在 父組件中,定義一個(gè)固定名稱的function,叫做 getChildContext ,內(nèi)部必須返回一個(gè)對象,就是要共享給 所有子孫自建的 數(shù)據(jù)
  getChildContext() {
    return {
      color: this.state.color
    }
  }

  // 2. 使用屬性校驗(yàn),規(guī)定一下傳遞給子組件的數(shù)據(jù)類型, 需要定義一個(gè)靜態(tài)的(static) childContextTypes(固定名稱,不要改)
  static childContextTypes = {
    color: ReactTypes.string // 規(guī)定了 傳遞給子組件的 數(shù)據(jù)類型
  }

子孫組件:先進(jìn)行屬性校驗(yàn),再通過 this.context.屬性名直接使用

  // 3. 上來之后,先來個(gè)屬性校驗(yàn),去校驗(yàn)一下父組件傳遞過來的參數(shù)類型
  static contextTypes = {
    color: ReactTypes.string // 這里,如果子組件想要使用父組件通過 context 共享的數(shù)據(jù),那么在使用之前,一定要先做一下數(shù)據(jù)類型校驗(yàn)
  }

  render() {
    return <div>
      <h5 style={{ color: this.context.color }}>這是 孫子組件  ---  {this.context.color} </h5>
    </div>
  }

記住一串單詞組合getChildContextTypes
父組件:前3個(gè)單詞(方法)、后3個(gè)單詞(靜態(tài)屬性)
子孫組件:后兩個(gè)單詞(靜態(tài)屬性)

相關(guān)文章

類型校驗(yàn)
Animation Add-Ons

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

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

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