State和組件的生命周期循環(huán)

考慮之前的例子褒翰,我們只學(xué)會(huì)了一種方法去更新UI斧蜕,我們調(diào)用ReactDOM.render()去改變輸出渲染:

function tick() {
  const element = (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {new Date().toLocaleTimeString()}.</h2>
    </div>
  );
  ReactDOM.render(
    element,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

在這個(gè)章節(jié)险耀,我們將會(huì)學(xué)到如何將Clock這個(gè)自定義組件變的真正可復(fù)用而且封裝完備觉痛,讓它可以在內(nèi)部設(shè)置它自己的時(shí)間定時(shí)器并且按照這個(gè)定時(shí)器時(shí)時(shí)更新它的UI荠瘪。
我們可以先看一看clock長(zhǎng)什么樣子:

function Clock(props) {
  return (
    <div>
      <h1>Hello, world!</h1>
      <h2>It is {props.date.toLocaleTimeString()}.</h2>
    </div>
  );
}

function tick() {
  ReactDOM.render(
    <Clock date={new Date()} />,
    document.getElementById('root')
  );
}

setInterval(tick, 1000);

然而上面這些代碼忽略了一個(gè)至關(guān)重要的需求:Clock的定時(shí)器必須在其內(nèi)部定義實(shí)現(xiàn)孕索。
我們想要的是只需要將Clock組件寫(xiě)一次它就可以自動(dòng)的更新它本身逛艰,像下面這樣:

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

為了實(shí)現(xiàn)我們所想要的效果,我們需要給Clock組件添加一個(gè)state屬性搞旭。
state和props看起來(lái)像是一樣的散怖,但是state是組件私有的并且完全受到組件自身控制。
在之前的章節(jié)我們提到過(guò)肄渗,類(lèi)式的聲明component會(huì)使定義的組件有一些額外的特性镇眷,state就是一個(gè),它只在類(lèi)式聲明的組件中起作用翎嫡。

將函數(shù)式聲明轉(zhuǎn)化為類(lèi)式聲明

你可以通過(guò)以下五個(gè)步驟將函數(shù)式聲明轉(zhuǎn)化為類(lèi)式聲明:

  1. 創(chuàng)建一個(gè)ES6的class欠动,這個(gè)class繼承自React.Component。
  2. 為這個(gè)類(lèi)添加一個(gè)名為render的空函數(shù)惑申。
  3. 將函數(shù)式聲明內(nèi)部的代碼移到render函數(shù)中具伍。
  4. 將render函數(shù)中的props替換成this.props。
  5. 將函數(shù)式聲明刪除圈驼。
class Clock extends React.Component {
 render() {
   return (
     <div>
       <h1>Hello, world!</h1>
       <h2>It is {this.props.date.toLocaleTimeString()}.</h2>
     </div>
   );
 }
}

現(xiàn)在Clock組件是一個(gè)類(lèi)式定義的組件了人芽,這使得你可以使用組件額外的特性,比如:state绩脆,鉤子函數(shù)萤厅。

給這個(gè)類(lèi)式聲明的組件添加state

我們將props對(duì)象中的state轉(zhuǎn)化成state需要經(jīng)過(guò)三個(gè)步驟:

  1. 在render函數(shù)中將this.props.date用this.state.date代替:
class Clock extends React.Component {
  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}
  1. 添加一個(gè)類(lèi)構(gòu)造器,在構(gòu)造器中指定this.state的初始值:
class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

注意我們?nèi)绾螌rops傳遞給構(gòu)造器:

constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

類(lèi)式聲明的組件必須調(diào)用super(props)這行代碼靴迫。

  1. 將Clock組件聲明中去掉date:
ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

待會(huì)我們會(huì)添加一個(gè)定時(shí)器的代碼給組件√栉叮現(xiàn)在我們所有完成的代碼看起來(lái)是下面這個(gè)樣子:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

接下來(lái)我們?yōu)镃lock組件設(shè)置自己的定時(shí)器并且依照這個(gè)定時(shí)器實(shí)時(shí)更新它自己。

為組件添加生命周期函數(shù)

在實(shí)際應(yīng)用中玉锌,對(duì)于組件來(lái)說(shuō)非常重要的一點(diǎn)是在它被銷(xiāo)毀的時(shí)候釋放自身的資源名挥。
我們想要為Clock組件設(shè)置一個(gè)定時(shí)器以便Clock組件在任何被聲明的時(shí)候可以立即執(zhí)行這個(gè)定時(shí)器,這段代碼被寫(xiě)在一個(gè)稱(chēng)之為裝載函數(shù)的內(nèi)部芬沉。
我們也想在這個(gè)組件被移除時(shí)銷(xiāo)毀這個(gè)定時(shí)器躺同,這段代碼被寫(xiě)在一個(gè)稱(chēng)之為卸載函數(shù)的內(nèi)部。
我們可以在class里聲明兩個(gè)不同的方法以實(shí)現(xiàn)組件的裝載和卸載:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {

  }

  componentWillUnmount() {

  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

這些方法被稱(chēng)為生命循環(huán)鉤子函數(shù)丸逸。
componentDidMount()函數(shù)在組件被渲染到dom上時(shí)執(zhí)行蹋艺,這個(gè)我們?cè)O(shè)置定時(shí)器的絕佳地方:

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

注意看看我們是如何將定時(shí)器ID保存在this中的。
當(dāng)this.props被組件自己設(shè)置且this.state有一個(gè)特殊的含義時(shí)黄刚,你就可以手動(dòng)的為這個(gè)類(lèi)添加你需要儲(chǔ)存的東西捎谨,這些你添加的東西是不會(huì)被輸出到外部的。
你在render函數(shù)中用不到的東西不應(yīng)該出現(xiàn)在state中憔维。
我們將清除定時(shí)器的代碼寫(xiě)在componentWillUnmount函數(shù)中:

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

最后涛救,我們實(shí)現(xiàn)tick方法。tick方法用this.setState函數(shù)來(lái)實(shí)時(shí)state:

class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: new Date()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: new Date()
    });
  }

  render() {
    return (
      <div>
        <h1>Hello, world!</h1>
        <h2>It is {this.state.date.toLocaleTimeString()}.</h2>
      </div>
    );
  }
}

ReactDOM.render(
  <Clock />,
  document.getElementById('root')
);

現(xiàn)在tick函數(shù)每過(guò)一秒便被執(zhí)行一次业扒。讓我們快速整理一下上面的代碼:

  1. 首先我們將clock組件作為參數(shù)傳遞給ReactDOM.render()函數(shù)检吆,這時(shí)候react會(huì)調(diào)用clock組件中的構(gòu)造器,在構(gòu)造器中初始化了state程储。
  2. react這時(shí)會(huì)調(diào)用組件中的render方法蹭沛,這個(gè)方法的返回值即為react渲染到屏幕上的element。
  3. 當(dāng)clock的返回值被插入到dom中時(shí)章鲤,react會(huì)開(kāi)始執(zhí)行componentDidMount這個(gè)方法摊灭。在這個(gè)方法內(nèi)部,我們定義了一個(gè)tick的函數(shù)實(shí)時(shí)的更新時(shí)間败徊。
  4. 每一秒鐘clcok都會(huì)執(zhí)行tick這個(gè)函數(shù)帚呼,在tick函數(shù)內(nèi)部,我們每一秒都會(huì)用當(dāng)前時(shí)間去替換this.state.date的值皱蹦。
  5. 如果clock組件被移除dom時(shí)煤杀,react 將會(huì)調(diào)用componentWillUnmount函數(shù)去移除我們?cè)赾omponentDidMount中定義的定時(shí)器。

正確使用State

對(duì)于setState函數(shù)根欧,你必須知道以下三點(diǎn):
** 不要直接修改State **
比如怜珍,下面這行代碼不會(huì)改變component:

// Wrong
this.state.comment = 'Hello';

你需要用下面這行代碼代替:

// Correct
this.setState({comment: 'Hello'});

你唯一可以定義state的地方便是constructor。
** state可能是異步更新的 **
react可能會(huì)因?yàn)樾阅芏淮涡缘膱?zhí)行多個(gè)setState函數(shù)凤粗。正是由于this.props和this.state會(huì)異步更新酥泛,所以你不能直接用它們的值來(lái)進(jìn)行計(jì)算,比如下面的例子你可能得不到想要的結(jié)果:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

為了讓值確定嫌拣,我們需要用到setState函數(shù)的第二種傳參方式柔袁,傳遞一個(gè)函數(shù)進(jìn)去而不是對(duì)象。這個(gè)函數(shù)將以前的state作為它的第一個(gè)參數(shù)异逐,props作為第二個(gè)參數(shù):

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

上面是我們使用了尖頭函數(shù)捶索,它和普通的函數(shù)沒(méi)什么區(qū)別:

// Correct
this.setState(function(prevState, props) {
  return {
    counter: prevState.counter + props.increment
  };
});

State更新是合并性更新

當(dāng)你調(diào)用setState函數(shù)時(shí),react會(huì)合并你此次設(shè)置的state和原來(lái)的state灰瞻。比如腥例,你的state包含以下2個(gè)變量:

 constructor(props) {
    super(props);
    this.state = {
      posts: [],
      comments: []
    };
  }

你可以單獨(dú)的更新它們:

componentDidMount() {
    fetchPosts().then(response => {
      this.setState({
        posts: response.posts
      });
    });

    fetchComments().then(response => {
      this.setState({
        comments: response.comments
      });
    });
  }

數(shù)據(jù)流

不管是根組件或者子組件辅甥,它們都不知道一個(gè)組件是有狀態(tài)的還是無(wú)狀態(tài)的,也不知道這個(gè)組件使用類(lèi)式定義的函數(shù)函數(shù)式定義的燎竖。這就是為什么state被稱(chēng)為本地的或者被封裝的璃弄。一個(gè)組件可以選擇是否將其state作為props傳遞給子組件:

<h2>It is {this.state.date.toLocaleTimeString()}.</h2>

這一特性同樣也在自定義組件中工作正常:

<FormattedDate date={this.state.date} />

FormattedDate組件將date作為其props,但是它并不知道date是來(lái)自于Clock的state构回,Clock的props或者手動(dòng)輸入的值:

function FormattedDate(props) {
  return <h2>It is {props.date.toLocaleTimeString()}.</h2>;
}

這一特性通常被稱(chēng)為數(shù)據(jù)流夏块。一些state通常被一些特定的component所有,所以這些組件之下的用到這些state的子組件會(huì)受到這些特殊組件的影響纤掸。為了展示所以的組件都是獨(dú)立的脐供,我們可以創(chuàng)建一個(gè)擁有3個(gè)Clock組件的應(yīng)用:

function App() {
  return (
    <div>
      <Clock />
      <Clock />
      <Clock />
    </div>
  );
}

ReactDOM.render(
  <App />,
  document.getElementById('root')
);

每一個(gè)clock組件都有它自己的定時(shí)器。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末借跪,一起剝皮案震驚了整個(gè)濱河市政己,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌掏愁,老刑警劉巖匹颤,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異托猩,居然都是意外死亡印蓖,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門(mén)京腥,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)赦肃,“玉大人,你說(shuō)我怎么就攤上這事公浪∷穑” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵欠气,是天一觀的道長(zhǎng)厅各。 經(jīng)常有香客問(wèn)我,道長(zhǎng)预柒,這世上最難降的妖魔是什么队塘? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮宜鸯,結(jié)果婚禮上憔古,老公的妹妹穿的比我還像新娘。我一直安慰自己淋袖,他們只是感情好鸿市,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般焰情。 火紅的嫁衣襯著肌膚如雪陌凳。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,036評(píng)論 1 285
  • 那天内舟,我揣著相機(jī)與錄音冯遂,去河邊找鬼。 笑死谒获,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的壁却。 我是一名探鬼主播批狱,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼展东!你這毒婦竟也來(lái)了赔硫?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤盐肃,失蹤者是張志新(化名)和其女友劉穎爪膊,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體砸王,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡推盛,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了谦铃。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片耘成。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖驹闰,靈堂內(nèi)的尸體忽然破棺而出瘪菌,到底是詐尸還是另有隱情,我是刑警寧澤嘹朗,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布师妙,位于F島的核電站,受9級(jí)特大地震影響屹培,放射性物質(zhì)發(fā)生泄漏默穴。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一褪秀、第九天 我趴在偏房一處隱蔽的房頂上張望壁顶。 院中可真熱鬧,春花似錦溜歪、人聲如沸若专。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)调衰。三九已至膊爪,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間嚎莉,已是汗流浹背米酬。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留趋箩,地道東北人赃额。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像叫确,于是被迫代替她去往敵國(guó)和親跳芳。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

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

  • 想一下上一節(jié)中那個(gè)滴答計(jì)時(shí)的例子竹勉。迄今為止飞盆,我們只學(xué)到一種更新UI的方法。我們通過(guò)調(diào)用ReactDOM.rende...
    莫銘閱讀 510評(píng)論 0 0
  • React版本:15.4.2**翻譯:xiyoki ** 考慮前一節(jié)中滴答作響的時(shí)鐘的例子次乓。到目前為止吓歇,我們只學(xué)習(xí)...
    前端xiyoki閱讀 388評(píng)論 0 0
  • State 和生命周期 考慮前面章節(jié)中時(shí)鐘的例子。 到目前位置票腰,我們僅學(xué)習(xí)了一種更新 UI 的方式城看。 我們調(diào)用Re...
    soojade閱讀 1,203評(píng)論 0 1
  • 學(xué)習(xí)使用Clock組件,來(lái)重用和封裝杏慰。并設(shè)置定時(shí)器析命。封裝了一個(gè)定時(shí)器,如: Try it on CodePen. ...
    ZMJun閱讀 448評(píng)論 0 0
  • 今天小寶一放學(xué)就去同學(xué)天禹家玩了逃默,一直玩到8點(diǎn)半才回家鹃愤,我去接他時(shí),看見(jiàn)他抱著好幾把槍?zhuān)隙ㄊ撬褎e人的玩具借回...
    蝶舞心間閱讀 90評(píng)論 0 0