摘要:接收一個屬性,這個組件會讓后代組件統(tǒng)一提供這個變量值。因此對于同一個對象而言,一定是后代元素。解決方法就是把內(nèi)聯(lián)函數(shù)提取出來,如下講了這么多,我們還沒有講到其實我們已經(jīng)講完了的工作原理了。
本節(jié)主要講解以下幾個新的特性:
Context
ContextType
lazy
Suspense
錯誤邊界(Error boundaries)
memo
想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你!
Context定義:Context 提供了一種方式,能夠讓數(shù)據(jù)在組件樹中傳遞而不必一級一級手動傳遞。
這定義讀的有點晦澀,來看張圖:
假設(shè)有如上的組件層級關(guān)系,如果最底層的 Item 組件,需要最頂層的 Window 組件中的變量,那我們只能一層一層的傳遞下去。非常的繁瑣,最重要的是中間層可能不需要這些變量。
有了 Context 之后,我們傳遞變量的方式是這樣的:
Item 可以直接從 Window 中獲取變量值。
當(dāng)然這種方式會讓組件失去獨立性,復(fù)用起來更困難。不過存在即合理,一定有 Context 適用場景。那 Context 是如何工作的呢。
首先要有一個 Context 實例對象,這個對象可以派生出兩個 React 組件,分別是 Provier 和 Consumer。
Provider 接收一個 value 屬性,這個組件會讓后代組件統(tǒng)一提供這個變量值。當(dāng)然后代組件不能直接獲取這個變量,因為沒有途徑。所以就衍生出 Consumer 組件,用來接收 Provier 提供的值。
一個 Provider 可以和多個消費組件有對應(yīng)關(guān)系。多個 Consumer 也可以嵌套使用,里層的會覆蓋外層的數(shù)據(jù)。
因此對于同一個 Context 對象而言,Consumer 一定是 Provier 后代元素。
創(chuàng)建 Contect 方式如下:
const MyContext = React.createContext(defaultValue?);
來個實例:
import React, {createContext, Component} from "react"; const BatteryContext = createContext(); class Leaf extends Component { render() { return ({ battery => ); } } // 為了體現(xiàn)層級多的關(guān)系,增加一層 Middle 組件 class Middle extends Component { render() { returnBattery: {battery}
}} } class App extends Component { render () { return ( ) } } export default App;
上述,首先創(chuàng)建一個 Context 對象 BatteryContext, 在 BatteryContext.Provider 組件中渲染 Middle 組件,為了說明一開始我們所說的多層組件關(guān)系,所以我們在 Middle 組件內(nèi)不直接使用 BatteryContext.Consumer。而是在 其內(nèi)部在渲染 Leaf 組件,在 Leaf 組件內(nèi)使用 BatteryContext.Consumer 獲取BatteryContext.Provider 傳遞過來的 value 值。
運行結(jié)果:
當(dāng) Provider 的 value 值發(fā)生變化時,它內(nèi)部的所有消費組件都會重新渲染。Provider 及其內(nèi)部 consumer 組件都不受制于 shouldComponentUpdate 函數(shù),因此當(dāng) consumer 組件在其祖先組件退出更新的情況下也能更新。
來個實例:
... class App extends Component { state = { battery: 60 } render () { const {battery} = this.state; return () } } ...
首先在 App 中的 state 內(nèi)聲明一個 battery 并將其傳遞給 BatteryContext.Provider 組件,通過 button 的點擊事件進(jìn)減少 一 操作。
運行效果 :
同樣,一個組件可能會消費多個 context,來演示一下:
import React, {createContext, Component} from "react"; const BatteryContext = createContext(); const OnlineContext = createContext(); class Leaf extends Component { render() { return ({ battery => ( ); } } // 為了體現(xiàn)層級多的關(guān)系,增加一層 Middle 組件 class Middle extends Component { render() { return{ online => ) }Battery: {battery}, Online: {String(online)}
}} } class App extends Component { state = { online: false, battery: 60 } render () { const {battery, online} = this.state; console.log("render") return ( ) } } export default App;
同 BatteryContext 一樣,我們在聲明一個 OnlineContext,并在 App state 中聲明一個 online 變量,在 render 中解析出 online。如果有多個 Context 的話,只要把對應(yīng)的 Provier 嵌套進(jìn)來即可,順序并不重要。同樣也加個 button 來切換 online 的值。
接著就是使用 Consumer,與 Provier 一樣嵌套即可,順序一樣不重要,由于 Consumer 需要聲明函數(shù),語法稍微復(fù)雜些。
運行結(jié)果:
接下來在 App 中注釋掉
//
在看運行效果:
可以看出,并沒有報錯,只是 battery 取不到值。這時候 createContext() 的默認(rèn)值就派上用場了,用以下方式創(chuàng)建:
const BatteryContext = createContext(90);
這個默認(rèn)值的使用場景就是在 Consumer 找不到 Provier 的時候。當(dāng)然一般業(yè)務(wù)是不會有這種場景的。
ContextType... class Leaf extends Component { render() { return ({ battery => ); } } ...Battery: {battery}
}
回到一開始的實例,我們在看下 Consuer 里面的實現(xiàn)。由于 Consumer 特性,里面的 JSX 必須是該 Consumer 的回返值。這樣的代碼就顯得有點復(fù)雜。我們希望在整個 JSX 渲染之前就能獲取 battery 的值。所以 ContextType 就派上用場了。這是一個靜態(tài)變量,如下:
... class Leaf extends Component { static contextType = BatteryContext; render() { const battery = this.context; return (Battery: {battery}
); } } ...
掛載在 class 上的 contextType 屬性會被重賦值為一個由 React.createContext() 創(chuàng)建的 Context 對象。這能讓你使用 this.context 來消費最近 Context 上的那個值。你可以在任何生命周期中訪問到它,包括 render 函數(shù)中。
你只通過該 API 訂閱單一 context。如果你想訂閱多個,就只能用較復(fù)雜的寫法了。lazy 和 Supense 的使用
React.lazy 函數(shù)能讓你像渲染常規(guī)組件一樣處理動態(tài)引入(的組件)。
首先聲明一個 About 組件
import React, {Component} from "react" export default class About extends Component { render () { returnAbout} }
然后在 APP 中使用 lazy 動態(tài)導(dǎo)入 About 組件:
import React, {Component, lazy, Suspense} from "react" const About = lazy(() => import(/*webpackChunkName: "about" */"./About.jsx")) class App extends Component { render() { return (); } } export default App;
運行后會發(fā)現(xiàn):
因為 App 渲染完成后,包含 About 的模塊還沒有被加載完成,React 不知道當(dāng)前的 About 該顯示什么。我們可以使用加載指示器為此組件做優(yōu)雅降級。這里我們使用 Suspense 組件來解決。
只需將異步組件 About 包裹起來即可。
...Loading...
fallback 屬性接受任何在組件加載過程中你想展示的 React 元素。你可以將 Suspense 組件置于懶加載組件之上的任何位置。你甚至可以用一個 Suspense 組件包裹多個異步組件。
那如果 about 組件加載失敗會發(fā)生什么呢?
上面我們使用 webpackChunkName 導(dǎo)入的名加載的時候取個一個名字 about,我們看下網(wǎng)絡(luò)請求,右鍵點擊 Block Request URL
重新加載頁面后,會發(fā)現(xiàn)整個頁面都報錯了:
在實際業(yè)務(wù)開發(fā)中,我們肯定不能忽略這種場景,怎么辦呢?
錯誤邊界(Error boundaries)如果模塊加載失敗(如網(wǎng)絡(luò)問題),它會觸發(fā)一個錯誤。你可以通過錯誤邊界技術(shù)來處理這些情況,以顯示良好的用戶體驗并管理恢復(fù)事宜。
如果一個 class 組件中定義了 static getDerivedStateFromError() 或 componentDidCatch() 這兩個生命周期方法中的任意一個(或兩個)時,那么它就變成一個錯誤邊界。當(dāng)拋出錯誤后,請使用 static getDerivedStateFromError() 渲染備用 UI ,使用 componentDidCatch() 打印錯誤信息。
接著,借用錯誤邊界,我們來優(yōu)化以上當(dāng)異步組件加載失敗的情況:
class App extends Component { state = { hasError: false, } static getDerivedStateFromError(e) { return { hasError: true }; } render() { if (this.state.hasError) { returnerror} return (}>Loading...
運行效果:
memo先來看個例子:
class Foo extends Component { render () { console.log("Foo render"); return null; } } class App extends Component { state = { count: 0 } render() { return (); } }
例子很簡單聲明一個 Foo 組件,并在 APP 的 state 中聲明一個變量 count ,然后通過按鈕更改 count 的值。
運行結(jié)果:
可以看出 count 值每變化一次, Foo 組件都會重新渲染一次,即使它沒有必要重新渲染,這個是我們的可以優(yōu)化點。
React 中提供了一個 shouldComponentUpdate,如果這個函數(shù)返回 false,就不會重新渲染。在 Foo 組件中,這里判斷只要傳入的 name 屬性沒有變化,就表示不用重新渲染。
class Foo extends Component { ... shouldComponentUpdate (nextProps, nextState) { if (nextProps.name === this.props.name) { return false } return true } ... }
運行效果:
Foo 組件不會重新渲染了。但如果我們傳入數(shù)據(jù)有好多個層級,我們得一個一個的對比,顯然就會很繁瑣且冗長。 其實 React 已經(jīng)幫我們提供了現(xiàn)層的對比邏輯就是 PureComponent 組件。我們讓 Foo 組件繼承 PureComponent
... class Foo extends PureComponent { render () { console.log("Foo render"); return null; } } ...
運行效果同上。但它的實現(xiàn)還是有局限性的,只有傳入屬性本身的對比,屬性的內(nèi)部發(fā)生了變化,它就搞不定了。來個粟子:
class Foo extends PureComponent { render () { console.log("Foo render"); return{this.props.person.age}; } } class App extends Component { state = { person: { count: 0, age: 1 } } render() { const {person} = this.state; return (); } }
在 App 中聲明一個 person,通過點擊按鈕更改 person 中的age屬性,并把 person 傳遞給 Foo 組件,在 Foo 組件中顯示 age。
運行效果:
點擊按鍵后,本應(yīng)該重新渲染的 Foo 組件,卻沒有重新渲染。就是因為 PureComponent 提供的 shouldComponentUpdate 發(fā)現(xiàn)的 person 本身沒有變化,才拒絕重新渲染。
所以一定要注意 PureComponent 使用的場景。只有傳入的 props 第一級發(fā)生變化,才會觸發(fā)重新渲染。所以要注意這種關(guān)系,不然容易發(fā)生視圖不渲染的 bug。
PureComponent 還有一個陷阱,修改一下上面的例子,把 age 的修改換成對 count,然后在 Foo 組件上加一個回調(diào)函數(shù):
... return (); ...{}}/>
運行效果:
可以看到 Foo 組件每次都會重新渲染,雖然 person 本身沒有變化,但是傳入的內(nèi)聯(lián)函數(shù)每次都是新的。
解決方法就是把內(nèi)聯(lián)函數(shù)提取出來,如下:
... callBack = () => {}...
講了這么多,我們還沒有講到 memo,其實我們已經(jīng)講完了 memo 的工作原理了。
React.memo 為高階組件。它與 React.PureComponent 非常相似,但它適用于函數(shù)組件,但不適用于 class 組件。
我們 Foo 組件并沒有相關(guān)的狀態(tài),所以可以用函數(shù)組件來表示。
... function Foo (props) { console.log("Foo render"); return{props.person.age}; } ...
接著使用 memo 來優(yōu)化 Foo 組件
... const Foo = memo(function Foo (props) { console.log("Foo render"); return{props.person.age}; }) ...
運行效果
最后,如果你喜歡這個系列的,肯請大家給個贊的,我將會更有的動力堅持寫下去。
參考React 官方文檔
《React勁爆新特性Hooks 重構(gòu)去哪兒網(wǎng)》
交流干貨系列文章匯總?cè)缦拢X得不錯點個Star,歡迎 加群 互相學(xué)習(xí)。
https://github.com/qq44924588...
我是小智,公眾號「大遷世界」作者,對前端技術(shù)保持學(xué)習(xí)愛好者。我會經(jīng)常分享自己所學(xué)所看的干貨,在進(jìn)階的路上,共勉!
關(guān)注公眾號,后臺回復(fù)福利,即可看到福利,你懂的。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/104610.html
摘要:粟例說明一下獲取子組件或者節(jié)點的句柄指向已掛載到上的文本輸入元素本質(zhì)上,就像是可以在其屬性中保存一個可變值的盒子。粟例說明一下渲染周期之間的共享數(shù)據(jù)的存儲上述使用聲明兩個副作用,第一個每隔一秒對加,因為只需執(zhí)行一次,所以每二個參為空數(shù)組。 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! React 新特性講解及實例(一) React 新特性 Hooks 講解及實...
摘要:來個使用類形式的例子以上就不說解釋了,第一篇已經(jīng)講過了,接著將改成用的形式接著使用形式接收一個對象的返回值并返回該的當(dāng)前值。如果的返回值是函數(shù)的話,那么就可以簡寫成的方式,只是簡寫而已,實際并沒有區(qū)別。 本文是 React 系列的第三篇 React 新特性講解及實例(一) React 新特性 Hooks 講解及實例(二) 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著...
摘要:還可以返回另一個回調(diào)函數(shù),這個函數(shù)的執(zhí)行時機(jī)很重要。對于第二個我們可以通過返回一個回調(diào)函數(shù)來注銷事件的注冊。回調(diào)函數(shù)在視圖被銷毀之前觸發(fā),銷毀的原因有兩種重新渲染和組件卸載。 本文是 React 系列的第二篇 React 新特性講解及實例(一) 想閱讀更多優(yōu)質(zhì)文章請猛戳GitHub博客,一年百來篇優(yōu)質(zhì)文章等著你! 什么是 Hooks Hook 是 React 16.8 的新增特性。它可...
摘要:屬性是一個組件的外部輸入。只會在開發(fā)模式下進(jìn)行屬性類型檢查,當(dāng)代碼進(jìn)行生產(chǎn)發(fā)布后,為了減少額外的性能開銷,類型檢查將會被略過。某個類的實例枚舉,屬性值必須為其中的某一個值。屬性為一個數(shù)組,且數(shù)組中的元素必須符合指定類型。 在第二篇文章 《新型前端開發(fā)方式》 中有說到 React 有很爽的一點就是給我們一種創(chuàng)造 HTML 標(biāo)簽的能力,那么今天這篇文章就詳細(xì)講解下 React 是如何提供這...
摘要:屬性是一個組件的外部輸入。只會在開發(fā)模式下進(jìn)行屬性類型檢查,當(dāng)代碼進(jìn)行生產(chǎn)發(fā)布后,為了減少額外的性能開銷,類型檢查將會被略過。某個類的實例枚舉,屬性值必須為其中的某一個值。屬性為一個數(shù)組,且數(shù)組中的元素必須符合指定類型。 在第二篇文章 《新型前端開發(fā)方式》 中有說到 React 有很爽的一點就是給我們一種創(chuàng)造 HTML 標(biāo)簽的能力,那么今天這篇文章就詳細(xì)講解下 React 是如何提供這...
閱讀 1552·2021-11-17 09:33
閱讀 1100·2021-11-12 10:36
閱讀 2414·2019-08-30 15:54
閱讀 2441·2019-08-30 13:14
閱讀 2914·2019-08-26 14:05
閱讀 3289·2019-08-26 11:32
閱讀 3001·2019-08-26 10:09
閱讀 2995·2019-08-26 10:09