在 React 16 中使用高階組件來(lái)捕獲異常

可能你已經(jīng)知道, 在 React 16 中, 將會(huì)引進(jìn)一個(gè)全新的架構(gòu) - React Fiber, 它徹底重寫了 React 的協(xié)調(diào)算法, 并引入了一些新的特性. 這篇文章就是跟大家分享 React 16 中新的生命周期方法 - componentDidCatch, 它能捕獲在子組件樹中任何地方的 JavaScript 異常耿战,并打印這些錯(cuò)誤和展示備用UI, 就像將 children 包裹在一個(gè)大的 try/catch 語(yǔ)句塊中. 你可以閱讀 Dan Abramov 的 Error Handling in React 16 獲取更多關(guān)于 componentDidCatch 的內(nèi)容.

絕大多數(shù)的開發(fā)人員使用 React 16 都應(yīng)該是由 15 升級(jí)上來(lái)的, 然而, 為了使用錯(cuò)誤處理而去重寫整個(gè)組件肯定是不明智的, 那么, 該怎么辦呢, 當(dāng)然有更好的處理方式, 那就是想辦法封裝組件, 像修改組件定義那是逼上梁上的行為.

那么, 錯(cuò)誤處理究竟可以干些什么

  • 當(dāng)有錯(cuò)誤發(fā)生時(shí), 我們可以友好地展示 fallback 組件
  • 避免 normalfallback 組件耦合
  • 我們可以清楚地發(fā)現(xiàn)某些服務(wù)的一些報(bào)錯(cuò)
  • 我們可以復(fù)用報(bào)錯(cuò)fallback

接下來(lái), 我們看一個(gè)最森破的的實(shí)例

class ErrorHandler extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // 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;
  }
}

// Then you can use it as a regular component
const MyPage = props => (
  <Container>
    <ErrorHandler>
      <MyComponent />

      <MyOtherComponent />
    </ErrorHandler>
  </Container>
)

其實(shí), 我們還可以將其優(yōu)化一下, 比如將 ErrorHandler 參數(shù)化, 將 reportErrorToService 作為 props 傳遞, 用 component 替代 上面返回的幾行視圖代碼. 所以, 我們還是要更新我們的組件定義, 以便將 UI 的一部分添加 ErrorHandler 中. 但正如我們之前所討論的, 我們并不想修改組件的定義, 我們只 decorate 它們

對(duì)此, 我們可以使用高階組件

const MyFallbackComponent = props => (
  <h1>Something Went Wrong</h1>
)

// we'll talk about `withErrorHandler` later
const MyErrorHandler = withErrorHandler(
  reportErrorToService,
  MyFallbackComponent
)

const MyComponent = MyErrorHandler(props => (
  /* ... */
))

const MyOthercomponent = MyErrorHandler(props => (
  /* ... */
))

const MyPage = props => (
  <Container>
    <MyComponent />

    <MyOtherComponent />
  </Container>
)

我們可以使用 withErrorHandler HOC 來(lái)封裝組件, 使組件獲得錯(cuò)誤處理, 即當(dāng)錯(cuò)誤發(fā)生時(shí), 調(diào)用 reportErrorToService 并展示 fallback 組件, 從而, 我們帥氣地避免了大量組件的定義修改

然而, 我們還可以更帥氣. 當(dāng)服務(wù)報(bào)錯(cuò)跟 fallback 組件的視圖都基本相同時(shí), 我們可以像下樣一樣 export withErrorHandler HOC

import withErrorHandler from 'error-handler-hoc'

import reportErrorToService from '../services/errorReporter'
import FallbackView from '../components/Fallback/'

export default withErrorHandler(
  reportErrorToService,
  FallbackView
)

然后, 我們 export 封裝過(guò)后的組件就可以了

// MyComponent.jsx
import ErrorHandler from '../HOCs/ErrorHandler.js'

const MyComponent = props => (
  /* ... */
)

export default ErrorHandler(MyComponent)

// ====================
// MyOtherComponent.jsx
import ErrorHandler from '../HOCs/ErrorHandler.js'
import ...

const MyOtherComponent = props => (
  /* ... */
)

// you might already be using HOCs
export default compose(
  SomeOtherHOC,
  ErrorHandler
)(MyOtherComponent)

// ====================
// MyPage.jsx
import { MyComponent, MyOtherComponent } from './components'

const MyPage = () => (
  <Container>
    <MyComponent />
    <MyOtherComponent />
  </Container>
)

這樣, 我們就可以輕松地給組價(jià)添加錯(cuò)誤處理, 同樣, 我們也可以輕松地移除

那么, withErrorHandler 究竟是如何工作的呢, 其實(shí), 實(shí)現(xiàn)它還是挺簡(jiǎn)單的

function withErrorHandler (errorCallback, FallbackComponent, Component) {
  class WithErrorHandler extends React.Component {
    constructor () {
      super()

      // Construct the initial state
      this.state = {
        hasError: false,
        error: null,
        errorInfo: null
      }
    }

    componentDidCatch (error, info) {
      // Update state if error happens
      this.setState({ hasError: true, error, errorInfo: info })

      // Report errors
      errorCallback(error, info, this.props)
    }

    render () {
      // if state contains error we render fallback component
      if (this.state.hasError) {
        const { error, errorInfo } = this.state
        return (
          <FallbackComponent
            {...this.props}
            error={error}
            errorInfo={errorInfo}
          />
        )
      }

      return <Component {...this.props} />
    }
  }
  WithErrorHandler.displayName = `withErrorHandler(${Component.displayName})`
  return WithErrorHandler
}

總結(jié)

這個(gè)高階組件可以用來(lái)封裝任何組件, 捕獲所有異常, 你可以用其封裝整個(gè) page, 也可以用其封裝個(gè)別組件.

點(diǎn)擊該 repository 可以查看更多源碼

原文鏈接: Catching exceptions using Higher Order Components in React 16

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末耗拓,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子姻锁,更是在濱河造成了極大的恐慌,老刑警劉巖迂苛,帶你破解...
    沈念sama閱讀 218,858評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件沟绪,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡绷旗,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,372評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門副砍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人庄岖,你說(shuō)我怎么就攤上這事豁翎。” “怎么了隅忿?”我有些...
    開封第一講書人閱讀 165,282評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵心剥,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我背桐,道長(zhǎng)优烧,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,842評(píng)論 1 295
  • 正文 為了忘掉前任链峭,我火速辦了婚禮畦娄,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己熙卡,他們只是感情好杖刷,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,857評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著驳癌,像睡著了一般滑燃。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上颓鲜,一...
    開封第一講書人閱讀 51,679評(píng)論 1 305
  • 那天表窘,我揣著相機(jī)與錄音,去河邊找鬼甜滨。 笑死蚊丐,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的艳吠。 我是一名探鬼主播麦备,決...
    沈念sama閱讀 40,406評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼昭娩!你這毒婦竟也來(lái)了凛篙?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,311評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤栏渺,失蹤者是張志新(化名)和其女友劉穎呛梆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體磕诊,經(jīng)...
    沈念sama閱讀 45,767評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡填物,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了霎终。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滞磺。...
    茶點(diǎn)故事閱讀 40,090評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖莱褒,靈堂內(nèi)的尸體忽然破棺而出击困,到底是詐尸還是另有隱情,我是刑警寧澤广凸,帶...
    沈念sama閱讀 35,785評(píng)論 5 346
  • 正文 年R本政府宣布阅茶,位于F島的核電站,受9級(jí)特大地震影響谅海,放射性物質(zhì)發(fā)生泄漏脸哀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,420評(píng)論 3 331
  • 文/蒙蒙 一扭吁、第九天 我趴在偏房一處隱蔽的房頂上張望撞蜂。 院中可真熱鬧盲镶,春花似錦、人聲如沸谅摄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,988評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)送漠。三九已至顽照,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間闽寡,已是汗流浹背代兵。 一陣腳步聲響...
    開封第一講書人閱讀 33,101評(píng)論 1 271
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留爷狈,地道東北人植影。 一個(gè)月前我還...
    沈念sama閱讀 48,298評(píng)論 3 372
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像涎永,于是被迫代替她去往敵國(guó)和親思币。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,033評(píng)論 2 355

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