React 新特性

  1. React 中一個(gè)常見模式是為一個(gè)組件返回多個(gè)元素嘱能。Fragments 可以讓你聚合一個(gè)子元素列表吝梅,并且不在DOM中增加額外節(jié)點(diǎn)。
class Columns extends React.Component {
  render() {
    return (
      <React.Fragment>
        <td>Hello</td>
        <td>World</td>
      </React.Fragment>
    );
  }
}

注意:①key 是唯一可以傳遞給 Fragment 的屬性焰檩。② 在 React 中憔涉,<></><React.Fragment><React.Fragment/> 的語(yǔ)法糖订框。

  1. Strict Mode嚴(yán)格模式
    StrictMode是一個(gè)用以標(biāo)記出應(yīng)用中潛在問(wèn)題的工具析苫。與Fragment類似,StrictMode不會(huì)渲染任何真實(shí)的UI穿扳。它為其后代元素觸發(fā)額外的檢查和警告衩侥。
import { StrictMode, Component } from 'react'

class Child extends Component {
  // 以下三個(gè)函數(shù)在 React v16.3 已不被推薦,未來(lái)的版本會(huì)廢棄矛物。
  componentWillMount() {
    console.log('componentWillMount')
  }
  componentWillUpdate() {
    console.log('componentWillUpdate')
  }
  componentWillReceiveProps() {
    console.log('componentWillReceiveProps')
  }
  render() {
    return (
      <div />
    )
  }
}

export default class StrictModeExample extends Component {
  render() {
    return (
      <StrictMode>
        <Child />
      </StrictMode>
    )
  }
}

由于在StrictMode內(nèi)使用了三個(gè)即將廢棄的API茫死,打開控制臺(tái) ,可看到如下錯(cuò)誤提醒:

控制臺(tái)報(bào)錯(cuò)信息

注釋:嚴(yán)格模式檢查只在開發(fā)模式下運(yùn)行履羞,不會(huì)與生產(chǎn)模式?jīng)_突峦萎。

  1. createRef (v16.3)
    (1) 老版本ref使用方式
    ① 字符串形式: <input ref="input" />
    ② 回調(diào)函數(shù)形式:<input ref={input => (this.input = input)} />
    (2) 字符串形式缺點(diǎn)
    ① 需要內(nèi)部追蹤 refthis 取值,會(huì)使 React 稍稍變慢忆首。
    ② 有時(shí)候this與你想象的并不一致爱榔。
import React from 'react'

class Children extends React.Component {
  componentDidMount() {
    // <h1></h1>
    console.log('children ref', this.refs.titleRef)
  }
  render() {
    return (
      <div>
        {this.props.renderTitle()}
      </div>
    )
  }
}

class Parent extends React.Component {
  // 放入子組件渲染
  renderTitle = () => (
    <h1 ref='titleRef'>{this.props.title}</h1>
  )

  componentDidMount() {
    // undefined
    console.log('parent ref:', this.refs.titleRef)
  }
  render() {
    return (
      <Children renderTitle={this.renderTitle}></Children>
    )
  }
}

export default Parent

(3) createRef語(yǔ)法

import React from 'react'

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.inputRef = React.createRef();
  }

  render() {
    return <input type="text" ref={this.inputRef} />;
  }

  componentDidMount() {
    this.inputRef.current.focus();
  }
}

export default MyComponent
  1. 調(diào)用setState更新狀態(tài)時(shí),若之后的狀態(tài)依賴于之前的狀態(tài)糙及,推薦使用傳入函數(shù)形式详幽。
    語(yǔ)法:setState((prevState, props) => stateChange, [callback])
    例如,假設(shè)我們想通過(guò)props.step在狀態(tài)中增加一個(gè)值:
this.setState((prevState, props) => {
  return {counter: prevState.counter + props.step};
});
  1. 錯(cuò)誤邊界
    (1) 錯(cuò)誤邊界用于捕獲其子組件樹 JavaScript 異常浸锨,記錄錯(cuò)誤并展示一個(gè)回退的 UIReact 組件唇聘,避免整個(gè)組件樹異常導(dǎo)致頁(yè)面空白。
    (2) 錯(cuò)誤邊界在渲染期間柱搜、生命周期方法內(nèi)迟郎、以及整個(gè)組件樹構(gòu)造函數(shù)內(nèi)捕獲錯(cuò)誤。
    (3) 組件如果定義了static getDerivedStateFromError()componentDidCatch()中的任意一個(gè)或兩個(gè)生命周期方法 聪蘸。當(dāng)其子組件拋出錯(cuò)誤時(shí)谎亩,可使用static getDerivedStateFromError()更新state,可使用componentDidCatch()記錄錯(cuò)誤信息宇姚。
class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  static getDerivedStateFromError(error) {
    // Update state so the next render will show the fallback UI.
    return { hasError: true };
  }

  componentDidCatch(error, info) {
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }

    return this.props.children; 
  }
}

而后你可以像一個(gè)普通的組件一樣使用:

<ErrorBoundary>
  <MyWidget />
</ErrorBoundary>

注釋:getDerivedStateFromErrorcomponentDidCatch方法使用一個(gè)即可捕獲子組件樹錯(cuò)誤匈庭。

  1. Portal
    (1) Portals 提供了一種很好的將子節(jié)點(diǎn)渲染到父組件以外的 DOM 節(jié)點(diǎn)的方式。
    (2) 通過(guò) Portals 進(jìn)行事件冒泡
    盡管 portal 可以被放置在DOM 樹的任何地方浑劳,但在其他方面其行為和普通的 React 子節(jié)點(diǎn)行為一致阱持。一個(gè)從 portal 內(nèi)部會(huì)觸發(fā)的事件會(huì)一直冒泡至包含 React 樹 的祖先。
import React from 'react';
import ReactDOM from 'react-dom';

class Modal extends React.Component {
  render() {
    return this.props.clicks % 2 === 1
      ? this.props.children
      : ReactDOM.createPortal(
        this.props.children,
        document.getElementById('modal'),
      );
  }
}

class Child extends React.Component {
  render() {
    return (
      <button>Click</button>
    );
  }
}

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {clicks: 0};
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    this.setState(prevState => ({
      clicks: prevState.clicks + 1
    }));
  }

  render() {
    return (
      <div onClick={this.handleClick}>
        <p>Number of clicks: {this.state.clicks}</p>
        <Modal clicks={this.state.clicks}>
          <Child />
        </Modal>
      </div>
    );
  }
}

export default Parent

注釋:當(dāng)組件由portal渲染方式切換為普通渲染方式魔熏,會(huì)導(dǎo)致該組件被卸載之后重新渲染衷咽。組件放大功能如果通過(guò)portal方式實(shí)現(xiàn)鸽扁,放大前的狀態(tài)(滾動(dòng)位置、焦點(diǎn)位置等)無(wú)法保持镶骗。

  1. fiber介紹
    fiberReact 16中新的和解引擎桶现。它的主要目的是使虛擬DOM能夠進(jìn)行增量渲染。
    (1) 同步更新過(guò)程的局限
    當(dāng)React決定要加載或者更新組件樹時(shí)鼎姊,會(huì)做很多事骡和,比如調(diào)用各個(gè)組件的生命周期函數(shù),計(jì)算和比對(duì)Virtual DOM相寇,最后更新DOM樹慰于,這整個(gè)過(guò)程是同步進(jìn)行的。瀏覽器那個(gè)唯一的主線程都在專心運(yùn)行更新操作唤衫,無(wú)暇去做任何其他的事情婆赠。
    更新過(guò)程的函數(shù)調(diào)用棧

    (2) React Fiber的方式
    破解JavaScript中同步操作時(shí)間過(guò)長(zhǎng)的方法其實(shí)很簡(jiǎn)單——分片。
    React Fiber把更新過(guò)程碎片化佳励,執(zhí)行過(guò)程如下面的圖所示休里,每執(zhí)行完一段更新過(guò)程,就把控制權(quán)交還給React負(fù)責(zé)任務(wù)協(xié)調(diào)的模塊赃承,看看有沒(méi)有其他緊急任務(wù)要做妙黍,如果沒(méi)有就繼續(xù)去更新,如果有緊急任務(wù)楣导,那就去做緊急任務(wù)废境。
    維護(hù)每一個(gè)分片的數(shù)據(jù)結(jié)構(gòu),就是Fiber筒繁。
    更新過(guò)程的函數(shù)調(diào)用棧

    (3) React Fiber更新過(guò)程的兩個(gè)階段
    React Fiber中噩凹,一次更新過(guò)程會(huì)分成多個(gè)分片完成,所以完全有可能一個(gè)更新任務(wù)還沒(méi)有完成毡咏,就被另一個(gè)更高優(yōu)先級(jí)的更新過(guò)程打斷驮宴,這時(shí)候,優(yōu)先級(jí)高的更新任務(wù)會(huì)優(yōu)先處理完呕缭,而低優(yōu)先級(jí)更新任務(wù)所做的工作則會(huì)完全作廢堵泽,然后等待機(jī)會(huì)重頭再來(lái)。
    React Fiber一個(gè)更新過(guò)程被分為兩個(gè)階段(Phase):第一個(gè)階段Reconciliation Phase和第二階段Commit Phase恢总。
    在第一階段Reconciliation Phase迎罗,React Fiber會(huì)找出需要更新哪些DOM,這個(gè)階段是可以被打斷的片仿;但是到了第二階段Commit Phase纹安,那就一鼓作氣把DOM更新完,絕不會(huì)被打斷。
    (4) React Fiber對(duì)現(xiàn)有代碼的影響
    image.png

    因?yàn)榈谝浑A段的過(guò)程會(huì)被打斷而且“重頭再來(lái)”厢岂,就會(huì)造成第一階段中的生命周期函數(shù)在一次加載和更新過(guò)程中可能會(huì)被多次調(diào)用光督!
    第一個(gè)階段的四個(gè)生命周期函數(shù)中,componentWillReceiveProps塔粒、componentWillMountcomponentWillUpdate這三個(gè)函數(shù)可能包含副作用结借,所以當(dāng)使用React Fiber的時(shí)候一定要重點(diǎn)看這三個(gè)函數(shù)的實(shí)現(xiàn)。
    注釋:大家應(yīng)該都清楚進(jìn)程(Process)和線程(Thread)的概念卒茬,在計(jì)算機(jī)科學(xué)中還有一個(gè)概念叫做Fiber船老,英文含義就是“纖維”,意指比Thread更細(xì)的線,也就是比線程(Thread)控制得更精密的并發(fā)處理機(jī)制允蜈。
  2. 聲明周期變化
    v16.3 開始,原來(lái)的三個(gè)生命周期 componentWillMountcomponentWillUpdate岭佳、componentWillReceiveProps 將被廢棄,取而代之的是兩個(gè)全新的生命周期:
    static getDerivedStateFromProps
    getSnapshotBeforeUpdate
  3. getDerivedStateFromProps用法
    static getDerivedStateFromProps(nextProps, prevState)
    組件實(shí)例化后接受新屬性時(shí)將會(huì)調(diào)用getDerivedStateFromProps喂走。它應(yīng)該返回一個(gè)對(duì)象來(lái)更新狀態(tài)呼胚,或者返回null來(lái)表明新屬性不需要更新任何狀態(tài)。
    如果父組件導(dǎo)致了組件的重新渲染张惹,即使屬性沒(méi)有更新舀锨,這一方法也會(huì)被調(diào)用。
    如果你只想處理變化宛逗,你可能想去比較新舊值坎匿。調(diào)用this.setState()通常不會(huì)觸發(fā)getDerivedStateFromProps()
  4. getStapshotBeforeUpdate
    getSnapshotBeforeUpdate()在最新的渲染輸出提交給DOM前將會(huì)立即調(diào)用雷激。它讓你的組件能在當(dāng)前的值可能要改變前獲得它們替蔬。這一生命周期返回的任何值將會(huì) 作為參數(shù)被傳遞給componentDidUpdate()
class ScrollingList extends React.Component {
  listRef = React.createRef();

  getSnapshotBeforeUpdate(prevProps, prevState) {
    // Are we adding new items to the list?
    // Capture the current height of the list so we can adjust scroll later.
    if (prevProps.list.length < this.props.list.length) {
      return this.listRef.current.scrollHeight;
    }
    return null;
  }

  componentDidUpdate(prevProps, prevState, snapshot) {
    // If we have a snapshot value, we've just added new items.
    // Adjust scroll so these new items don't push the old ones out of view.
    if (snapshot !== null) {
      this.listRef.current.scrollTop +=
        this.listRef.current.scrollHeight - snapshot;
    }
  }

  render() {
    return (
      <div ref={this.listRef}>{/* ...contents... */}</div>
    );
  }
}

在上面的例子中屎暇,為了支持異步渲染承桥,在getSnapshotBeforeUpdate 中讀取scrollHeight而不是componentWillUpdate,這點(diǎn)很重要根悼。由于異步渲染凶异,在“渲染”時(shí)期(如componentWillUpdaterender)和“提交”時(shí)期(如getSnapshotBeforeUpdatecomponentDidUpdate)間可能會(huì)存在延遲。如果一個(gè)用戶在這期間做了像改變?yōu)g覽器尺寸的事挤巡,從componentWillUpdate中讀出的scrollHeight值將是滯后的剩彬。

  1. Context用法
import React from 'react'

const ThemeContext = React.createContext();

const ThemedButton = (props) => (
  <ThemeContext.Consumer>
    { context => <span style={{color: context}}>{props.text}</span> }
  </ThemeContext.Consumer>
)

const Toolbar = () => (
  <ThemeContext.Provider value='red'>
    <ThemedButton text="Context API"/>
  </ThemeContext.Provider>
)

export default Toolbar

注釋:在 Context.Consumer 中,children必須為函數(shù)矿卑。

參考資料

React中文文檔
深入React v16新特性(一)
深入React v16新特性(二)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末喉恋,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瀑晒,老刑警劉巖绍坝,帶你破解...
    沈念sama閱讀 212,454評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異苔悦,居然都是意外死亡轩褐,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,553評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門玖详,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)把介,“玉大人,你說(shuō)我怎么就攤上這事蟋座∞痔撸” “怎么了?”我有些...
    開封第一講書人閱讀 157,921評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵向臀,是天一觀的道長(zhǎng)巢墅。 經(jīng)常有香客問(wèn)我,道長(zhǎng)券膀,這世上最難降的妖魔是什么君纫? 我笑而不...
    開封第一講書人閱讀 56,648評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮芹彬,結(jié)果婚禮上蓄髓,老公的妹妹穿的比我還像新娘。我一直安慰自己舒帮,他們只是感情好会喝,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,770評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著玩郊,像睡著了一般肢执。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓦宜,一...
    開封第一講書人閱讀 49,950評(píng)論 1 291
  • 那天蔚万,我揣著相機(jī)與錄音,去河邊找鬼临庇。 笑死反璃,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的假夺。 我是一名探鬼主播淮蜈,決...
    沈念sama閱讀 39,090評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼已卷!你這毒婦竟也來(lái)了梧田?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,817評(píng)論 0 268
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎裁眯,沒(méi)想到半個(gè)月后鹉梨,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,275評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡穿稳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,592評(píng)論 2 327
  • 正文 我和宋清朗相戀三年存皂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片逢艘。...
    茶點(diǎn)故事閱讀 38,724評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡旦袋,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出它改,到底是詐尸還是另有隱情疤孕,我是刑警寧澤,帶...
    沈念sama閱讀 34,409評(píng)論 4 333
  • 正文 年R本政府宣布央拖,位于F島的核電站祭阀,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏爬泥。R本人自食惡果不足惜柬讨,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,052評(píng)論 3 316
  • 文/蒙蒙 一崩瓤、第九天 我趴在偏房一處隱蔽的房頂上張望袍啡。 院中可真熱鬧,春花似錦却桶、人聲如沸境输。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,815評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)嗅剖。三九已至,卻和暖如春嘁扼,著一層夾襖步出監(jiān)牢的瞬間信粮,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,043評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工趁啸, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留强缘,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,503評(píng)論 2 361
  • 正文 我出身青樓不傅,卻偏偏與公主長(zhǎng)得像旅掂,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子访娶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,627評(píng)論 2 350

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