React框架16版中的錯誤邊界
React框架16版已經出了,許多變化令人興奮。其中有一個功能讓我感到興奮,那就是改良過的錯誤處理方法。之前的版本在渲染網頁過程中,如果發生了執行時錯誤,那整個React框架就會處於一種被破壞的狀態。
現在在React 16中,大家就能使用錯誤邊界功能,而不用一發生錯誤就解除整個程式掛載了。把錯誤邊界看成是一種類似於程式設計中try-catch語句的機制,只不過是由React元件來實現的。
錯誤邊界是一種React元件。它及其子元件形成一個樹型結構,能捕獲JavaScript中所有位置的錯誤,記錄下錯誤,並且還能顯示一個後備介面,避免讓使用者直接看到元件樹的崩潰資訊。
這裡涉及到一種新的生命週期函式叫componentDidCatch(error, info)
。無論什麼樣的類元件,只要定義了這個函式,就成為了一個錯誤邊界。
有了錯誤邊界,即使某個元件的結果有錯誤,整個React程式掛載也不會被解除。只有出錯的那個元件會顯示一個後備介面,而整個程式仍然完全正常執行。
現在要演示下如何在React程式中使用錯誤邊界。我們要舉一個電子商務網站的例子,這個網站有一個產品清單網頁,顯示了正在銷售的所有產品。
最終形成的網頁應該看上去是這樣的:
注意最後一個產品卡,那裡的模板可以看到有個什麼錯誤。靠React 16的錯誤邊界功能,我們能夠將一個後備介面模板插入到那個特定的元件中去,然後整個程式仍然能正常渲染。
錯誤邊界類元件可以用以下方法建立:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
import React, { Component } from 'react'; import { Card, CardMedia, CardTitle, CardText } from 'react-toolbox/lib/card'; const style = { width: '350px', marginLeft: '20px', marginTop: '20px', display: 'inline-block' }; class ErrorBoundary extends Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { this.setState({ hasError: true }); } render() { if(this.state.hasError) { return ( <Card style={style}> <CardMedia aspectRatio="wide" image="https://cdn.dribbble.com/users/1078347/screenshots/2799566/oops.png" /> <CardTitle title="Sorry Something went wrong!!!" subtitle="Error catched by error boundary of react 16" /> </Card> ); } return this.props.children; } } export default ErrorBoundary; |
然後我們可以將任意一個元件包在錯誤邊界元件內部來捕獲錯誤並顯示後備介面。
比如:
1 2 3 |
<errorboundary> <productcard/> </errorboundary> |
這裡的componentDidCatch()
函式使用方法和JavaScript中的catch {}
程式碼塊差不多,但只能用於元件。只有類元件才可以成為錯誤邊界。
在componentDidCatch()
函式內部我們把hasError狀態設定為true。然後在渲染方法中檢查那個狀態。如果出錯狀態是真,就渲染後備介面;如果是false就把想渲染的React元件介面當作子元件介面渲染出來。
這個程式裡主要有三部分:
- ProductList Component 產品表元件( 智慧元件Smart Component)
- Product Component 產品元件 (展示元件Presentational Component)
- Header Component 標題元件(展示元件Presentational Component)
網頁樣式我用的是React工具箱 Toolbox。
ProductList元件程式碼如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
import React, { Component } from 'react'; import ErrorBoundary from './ErrorBoundary'; import Product from './Product'; export default class ProductList extends Component { constructor(props) { super(props); this.state = { products: [{ name: 'iPhone 7', price: 'Price: 650$', imageUrl: 'https://i.ytimg.com/vi/7Jd7P42qaFM/maxresdefault.jpg' }, { name: 'Tesla', price: 'Price: 950$', imageUrl: 'https://www.tesla.com/tesla_theme/assets/img/models/v1.0/slideshow/Red_Bay-1440.jpg?20171005' }, { name: 'Iron', price: 'Price: 50$', imageUrl: 'https://images-na.ssl-images-amazon.com/images/I/41BW0yDhVeL._SX355_.jpg' }, { name: 'The Kite Runner', price: 'Price: 30$', imageUrl: 'https://images.gr-assets.com/books/1484565687l/77203.jpg' }, { price: 'Price: 950$', imageUrl: 'https://www.tesla.com/tesla_theme/assets/img/models/v1.0/slideshow/Red_Bay-1440.jpg?20171005' }] }; } renderProducts() { return this.state.products && this.state.products.map((product) => { return ( <ErrorBoundary key={product.name}> <Product product={product} /> </ErrorBoundary> ); }); } render() { return ( <div className="productList" style={{ marginTop: '40px' }}> { this.renderProducts() } </div> ); } } |
可以看到產品元件是包裹在錯誤邊界內部的。
而Product元件的程式碼是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
import React from 'react'; import { Card, CardMedia, CardTitle, CardText } from 'react-toolbox/lib/card'; const style = { width: '350px', marginLeft: '20px', marginTop: '20px', display: 'inline-block' }; const Product = (props) => ( <Card style={style}> <CardMedia aspectRatio="wide" image={props.product.imageUrl} /> <CardTitle title={props.product.name.toUpperCase()} subtitle="Subtitle here" /> <CardText>{props.product.price}</CardText> </Card> ); export default Product; |
在上面Product.js的程式碼中,我們呼叫了props.product.name.toUpperCase()函式。
所以無論什麼時候,只要一個產品沒有名字,就會產生值為未定義undefined的物件呼叫toUpperCase函式的情況。這種錯誤發生在以前,最終就會導致整個react程式解除掛載。
但現在有了錯誤邊界,我們可以捕獲這些錯誤,並渲染一個後備介面。比如下面這個介面:
希望我演示的這個例子能幫助理解React 16的錯誤邊界。
React 16還有帶有很多非常棒的功能,我想在下一篇文章裡再討論一些其它的。
以上演示程式的整個程式碼可以在我的github網頁上找到,連結是:
想進一步閱讀相關資料,請看下面的連結: