React進(jìn)階(八)創(chuàng)建組件

思考一個(gè)問題:
在DOM結(jié)構(gòu)中的某一個(gè)div元素藻治,是一個(gè)對(duì)象嗎载矿?

如果第一時(shí)間無法確定的同學(xué)可以在瀏覽器的Console中創(chuàng)建并查看一個(gè)div元素限书,如下圖虫蝶。

image.png

很顯然,所有的DOM元素其實(shí)都是一個(gè)對(duì)象倦西,而每當(dāng)我們?cè)趆tml中寫入一個(gè)div元素時(shí)能真,其實(shí)就是相當(dāng)于new了一個(gè)div的實(shí)例。

<div></div>

// like
new div()

我想很多人其實(shí)沒有意識(shí)到這一點(diǎn)扰柠,在React中粉铐,也運(yùn)用了類似的理念。當(dāng)我們學(xué)習(xí)React時(shí)卤档,理解到這個(gè)知識(shí)點(diǎn)非常重要蝙泼,因?yàn)樵趆tml中,我們不需要?jiǎng)?chuàng)建DOM元素裆装,而在React中則不同踱承,我們通常情況下會(huì)創(chuàng)建自己的組件,然后在jsx中實(shí)例化哨免,這簡直像極了DOM元素的使用。

是不是感覺學(xué)到知識(shí)了昙沦,求贊求贊 ^ ^
標(biāo)識(shí) 業(yè)

那么理解了DOM元素的這個(gè)點(diǎn)琢唾,并且知道其實(shí)React元素其實(shí)是非常類似的,那么現(xiàn)在我們思考一下我們會(huì)如何創(chuàng)建React組件呢盾饮?很顯然采桃,React組件實(shí)際上就是一個(gè)對(duì)象懒熙,在ES6的語法中,我們使用class語法來創(chuàng)建對(duì)象普办。因此首先大概會(huì)如下寫:

class ReactDemo {
}

每一個(gè)React組件都內(nèi)含了許多共同的邏輯工扎,例如生命周期,狀態(tài)等衔蹲,因此必然會(huì)有基類來實(shí)現(xiàn)這些共通的邏輯肢娘,所以我們會(huì)繼承他 React.Component

class ReactDemo extends React.Component {

}

我們需要進(jìn)一步學(xué)習(xí)的舆驶,則就是這些組件共通的特性橱健,他們分別是:

  • 生命周期
  • state
  • props
  • ref

學(xué)完了這些知識(shí)點(diǎn),你就可以大膽的宣告React你已經(jīng)學(xué)會(huì)啦 ~

生命周期函數(shù) render

生命周期中沙廉,最常見的就是render拘荡。顧名思義,它就是用來渲染模板語言jsx的撬陵。狀態(tài)<state>與屬性<props>每次改動(dòng)珊皿,它都會(huì)執(zhí)行一次,因此它是執(zhí)行次數(shù)最多的生命周期方法巨税,同時(shí)通過優(yōu)化代碼邏輯亮隙,減少它的執(zhí)行次數(shù),也是優(yōu)化React程序性能的最常用手段垢夹。因?yàn)樵跓o意識(shí)中溢吻,你的邏輯可能會(huì)讓render無意義的執(zhí)行更多次。

因此創(chuàng)建組件的下一步果元,就是渲染模板語言

class ReactDemo extends React.Component {
  render() {
    return (
      <div>hello world!</div>
    )
  }
}

就這樣促王,我們已經(jīng)創(chuàng)建了一個(gè)最簡單的組件。我們已經(jīng)可以將組件ReactDemo運(yùn)用起來了而晒。

import { render } from 'react-dom';
import ReactDemo from './ReactDemo';

render(<ReactDemo />, document.querySelector('#root'));
生命周期函數(shù) componentWillMount

當(dāng)?shù)谝淮蝦ender執(zhí)行完蝇狼,則表示組件已經(jīng)完全創(chuàng)立。
因此如果我們想要在組件創(chuàng)建完成之前對(duì)組件做一些事情倡怎,那么我們可以使用componentWillMount迅耘,該組件的應(yīng)用場景相對(duì)比較少,我通常用它來初始化一些無副作用的數(shù)據(jù)监署。

無副作用:不會(huì)引發(fā)組件render函數(shù)的執(zhí)行

class ReactDemo extends React.Component {
  currentKey = ''
  
  componentWillMount() {
    this.currentKey = this.props.key || 'tony'
  }

  render() {
    return (
      <div>hello {this.currentKey}!</div>
    )
  }
}
生命周期函數(shù) componentDidMount

當(dāng)組件第一次render執(zhí)行完颤专,會(huì)通過componentDidMount告訴我們。意味著此時(shí)組件處于一個(gè)相對(duì)穩(wěn)定的狀態(tài)钠乏,我們可以處理更多的邏輯栖秕。例如獲取網(wǎng)絡(luò)請(qǐng)求的數(shù)據(jù)等。

class ReactDemo extends React.Component {
  componentDidMount() {
    axios.get('xxxxx').then(res => {
      // todo
    })
  }

  render() {
    return (
      <div>hello wrold!</div>
    )
  }
}
生命周期函數(shù) componentWillUnmount

當(dāng)我們切換頁面或者需要隱藏掉一些組件時(shí)晓避,組件會(huì)被銷毀簇捍,在銷毀之前只壳,componentWillUnmount會(huì)告訴我們。在這個(gè)鉤子函數(shù)中暑塑,我們常常會(huì)手動(dòng)處理一些掃尾工作吼句,例如取消某些事件監(jiān)聽,清除定時(shí)器事格,重置存儲(chǔ)在閉包中的引用等惕艳。

class ReactDemo extends React.Component {
  componentDidMount() {
    document.addEventListener(...)
  }

  componentWillUnmount() {
    document.removeEventListener(...)
  }

  render() {
    return (
      <div>hello wrold!</div>
    )
  }
}

需要注意的是,willMount, didMount, WillUnmount三個(gè)生命周期函數(shù)在組件的整個(gè)生命周期中僅執(zhí)行一次分蓖。
理解了這三個(gè)函數(shù)的使用尔艇,基本已經(jīng)可以應(yīng)對(duì)大多數(shù)場景。因此生命周期函數(shù)暫時(shí)先介紹到這里么鹤,后續(xù)再介紹其他的生命周期函數(shù)终娃。

我們想要讓一個(gè)組件呈現(xiàn)出來的樣子與邏輯是可控的,那么就必然會(huì)引入狀態(tài)管理的機(jī)制蒸甜。一個(gè)組件通常受控于內(nèi)部狀態(tài)state外部狀態(tài)props

每一個(gè)狀態(tài)的改動(dòng)棠耕,都會(huì)引起render函數(shù)執(zhí)行,這一點(diǎn)非常重要柠新。

內(nèi)部狀態(tài)state

每個(gè)組件實(shí)例的內(nèi)部狀態(tài)僅僅只需要管理當(dāng)前實(shí)例窍荧,因此state必然是掛載在構(gòu)造函數(shù)中,因此我們通常使用如下的方式初始化state

不懂這句的朋友就必須回到<前端基礎(chǔ)進(jìn)階>中去補(bǔ)充基礎(chǔ)知識(shí)了恨憎,這個(gè)點(diǎn)非常重要

class Demo extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      bar: true
    }
  }

  render() {
    return (
      <div>{this.state.bar ? 'true' : 'false'}</div>
    )
  }
}

正因?yàn)閟tate掛載中構(gòu)造函數(shù)中蕊退,知道babel編譯結(jié)果的同學(xué)應(yīng)該會(huì)明白,state的初始化還可以簡寫成如下方式:

class Demo extends React.Component {
  state = {
    bar: true
  }

  render() {
    return (
      <div>{this.state.bar ? 'true' : 'false'}</div>
    )
  }
}

如上所示憔恳,在使用時(shí)瓤荔,我們通過this.state來訪問state的值。
除此之外钥组,我們還可以通過this.setState的方式來改變state的值输硝。

class Demo extends React.Component {
  state = {
    data: []
  }

  componentDidMount() {
    axios.get('/group/users').then(res => {
      this.setState({
        data: res.data
      })
    })
  }

  render() {
    const { data } = this.state;
    return (
      <div>
        {data.map((item, key) => (
          <div key={key}>{item.name}</div>
        ))}
      </div>
    )
  }
}

在這個(gè)例子中,render函數(shù)會(huì)執(zhí)行兩次程梦,第一次為組件初始化時(shí)点把,data沒有數(shù)據(jù)。第二次是接口請(qǐng)求成功后屿附,我們手動(dòng)修改了state的值郎逃,引發(fā)一次render的修改。

在這里我們可以總結(jié)幾個(gè)非常有意思的點(diǎn):

  • 當(dāng)我們想要改變頁面時(shí)拿撩,不再直接修改DOM元素衣厘,而僅僅只是修改了state中的數(shù)據(jù)。這個(gè)思維的轉(zhuǎn)變也是React的核心所在压恒,它直接改變了我們前端的開發(fā)思維與開發(fā)模式
  • render會(huì)因?yàn)闋顟B(tài)的變化而多次執(zhí)行影暴,而狀態(tài)的變化不可預(yù)測,因此計(jì)算量大的邏輯通常不會(huì)放在render中來執(zhí)行探赫,否則會(huì)帶來更高的性能消耗
  • 當(dāng)組件狀態(tài)首次穩(wěn)定了之后我們才會(huì)嘗試去修改它型宙,因此通常我們會(huì)將數(shù)據(jù)請(qǐng)求放在componentDidMount中執(zhí)行
  • setState是一個(gè)異步操作。明白事件循環(huán)的同學(xué)知道了這個(gè)點(diǎn)之后坑定能夠瞬間明白它可能會(huì)帶來的麻煩與小坑伦吠。所以妆兑,如果還不知道事件循環(huán)的同學(xué)得回過頭去補(bǔ)補(bǔ)知識(shí)了

大家還可以深入思考一下,為什么setState需要異步的方式毛仪,如果就弄成同步的方式會(huì)引發(fā)什么麻煩搁嗓?<面試必備>

外部狀態(tài)props
<div className="foo" index="1" dataset="xxx"></div>

熟練使用html的同學(xué)對(duì)上面例子中的class, index, dataset肯定不會(huì)陌生。當(dāng)我們用這種方式嘗試從外部控制組件的呈現(xiàn)時(shí)箱靴,就需要用到props腺逛。在div組件的內(nèi)部,我們可以使用如下的方式訪問外部傳進(jìn)來的參數(shù)衡怀。

this.props.className
this.props.index
this.props.dataset

我們可以使用defaultProps來設(shè)置props的默認(rèn)值

例如我們想要?jiǎng)?chuàng)建一個(gè)方塊棍矛,在創(chuàng)建時(shí)我們可以隨意定制它的顏色,長寬抛杨。那么我們可以簡單如下實(shí)現(xiàn)够委。

class Rect extends React.Component {
  static defaultProps = {
    color: 'red',
    width: '10px',
    height: '10px'
  }

  render() {
    const { color, width, height } = this.props;

    const _style = {
      color, width, height
    }
    return (
      <div style={_style} />
    )
  }
}

<Rect />
<Rect color="orange" width="20px" height="40px" />

我們不能直接在內(nèi)部修改props的狀態(tài)
傳入到組件中的props發(fā)生變化時(shí),render函數(shù)仍然會(huì)執(zhí)行一次

生命周期函數(shù) componentWillReceiveProps

每次接收到新的props之前<組件初始化除外>怖现,componentWillReceiveProps都會(huì)執(zhí)行一次茁帽,因此我們可以利用它來判斷某些關(guān)鍵的props是否發(fā)生了變化。

在該函數(shù)中屈嗤,舊的props使用this.props訪問潘拨,而新的props會(huì)通過參數(shù)傳入

componentWillReceiveProps(nextProps) {
    if (this.props.bar === nextProps.bar) {
      return;
    }
  }

需要警惕的是,許多人常常在想不到其他辦法的情況下會(huì)在該函數(shù)中根據(jù)props來修改內(nèi)部狀態(tài)state恢共。這是一個(gè)非常危險(xiǎn)的行為战秋。特別是當(dāng)前組件擁有大量子元素時(shí),這種行為會(huì)給你的代碼性能帶來嚴(yán)峻的考驗(yàn)讨韭,甚至可能是異常災(zāi)難脂信。

// 例如隨便摘抄一個(gè)例子
componentWillReceiveProps: function(nextProps) {
  this.setState({
    likesIncreasing: nextProps.likeCount > this.props.likeCount
  });
}

我通常只在沒有什么子元素的組件中使用這個(gè)生命周期函數(shù),其他場景會(huì)想辦法避開這個(gè)雷區(qū)透硝。

另外update相關(guān)的幾個(gè)生命周期如果使用不善也會(huì)存在性能問題狰闪,因此也不建議大家在實(shí)踐中使用,非要使用時(shí)請(qǐng)千萬要謹(jǐn)慎

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末濒生,一起剝皮案震驚了整個(gè)濱河市埋泵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖丽声,帶你破解...
    沈念sama閱讀 211,123評(píng)論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件礁蔗,死亡現(xiàn)場離奇詭異,居然都是意外死亡雁社,警方通過查閱死者的電腦和手機(jī)浴井,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,031評(píng)論 2 384
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來霉撵,“玉大人磺浙,你說我怎么就攤上這事⊥狡拢” “怎么了撕氧?”我有些...
    開封第一講書人閱讀 156,723評(píng)論 0 345
  • 文/不壞的土叔 我叫張陵,是天一觀的道長喇完。 經(jīng)常有香客問我伦泥,道長,這世上最難降的妖魔是什么何暮? 我笑而不...
    開封第一講書人閱讀 56,357評(píng)論 1 283
  • 正文 為了忘掉前任奄喂,我火速辦了婚禮,結(jié)果婚禮上海洼,老公的妹妹穿的比我還像新娘跨新。我一直安慰自己,他們只是感情好坏逢,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,412評(píng)論 5 384
  • 文/花漫 我一把揭開白布域帐。 她就那樣靜靜地躺著,像睡著了一般是整。 火紅的嫁衣襯著肌膚如雪肖揣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,760評(píng)論 1 289
  • 那天浮入,我揣著相機(jī)與錄音龙优,去河邊找鬼。 笑死事秀,一個(gè)胖子當(dāng)著我的面吹牛彤断,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播易迹,決...
    沈念sama閱讀 38,904評(píng)論 3 405
  • 文/蒼蘭香墨 我猛地睜開眼宰衙,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了睹欲?” 一聲冷哼從身側(cè)響起供炼,我...
    開封第一講書人閱讀 37,672評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤一屋,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后袋哼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冀墨,經(jīng)...
    沈念sama閱讀 44,118評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,456評(píng)論 2 325
  • 正文 我和宋清朗相戀三年先嬉,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了轧苫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片楚堤。...
    茶點(diǎn)故事閱讀 38,599評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡疫蔓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出身冬,到底是詐尸還是另有隱情衅胀,我是刑警寧澤,帶...
    沈念sama閱讀 34,264評(píng)論 4 328
  • 正文 年R本政府宣布酥筝,位于F島的核電站滚躯,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏嘿歌。R本人自食惡果不足惜掸掏,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,857評(píng)論 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望宙帝。 院中可真熱鬧丧凤,春花似錦、人聲如沸步脓。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,731評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽靴患。三九已至仍侥,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鸳君,已是汗流浹背农渊。 一陣腳步聲響...
    開封第一講書人閱讀 31,956評(píng)論 1 264
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留或颊,地道東北人砸紊。 一個(gè)月前我還...
    沈念sama閱讀 46,286評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像饭宾,于是被迫代替她去往敵國和親批糟。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,465評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容