前言
首先歡迎大家關(guān)注我的Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫東西沒法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì),希望大家多多關(guān)注呀!好久已經(jīng)沒寫React,發(fā)現(xiàn)連Context都發(fā)生了變化,忽然有一種村里剛通上的網(wǎng)的感覺,可能文章所提及的知識(shí)點(diǎn)已經(jīng)算是過時(shí)了,僅僅算作是自己的學(xué)習(xí)體驗(yàn)吧,
Context對(duì)于React開發(fā)者而言,Context應(yīng)該是一個(gè)不陌生的概念,但是在16.3之前,React官方一直不推薦使用,并聲稱該特性屬于實(shí)驗(yàn)性質(zhì)的API,可能會(huì)從之后的版本中移除。但是在實(shí)踐中非常多的第三方庫都基于該特性,例如:react-redux、mobx-react。
如上面的組件樹中,A組件與B組件之間隔著非常多的組件,假如A組件希望傳遞給B組件一個(gè)屬性,那么不得不使用props將屬性從A組件歷經(jīng)一系列中間組件最終跋山涉水傳遞給B組件。這樣代碼不僅非常的麻煩,更重要的是中間的組件可能壓根就用不上這個(gè)屬性,卻要承擔(dān)一個(gè)傳遞的職責(zé),這是我們不希望看見的。Context出現(xiàn)的目的就是為了解決這種場(chǎng)景,使得我們可以直接將屬性從A組件傳遞給B組件。
Legacy Context這里所說的老版本Context指的是React16.3之前的版本所提供的Context屬性,在我看來,這種Context是以一種協(xié)商聲明的方式使用的。作為屬性提供者(Provider)需要顯式聲明哪些屬性可以被跨層級(jí)訪問并且需要聲明這些屬性的類型。而作為屬性的使用者(Consumer)也需要顯式聲明要這些屬性的類型。官方文檔中給出了下面的例子:
import React, {Component} from "react"; import PropTypes from "prop-types"; class Button extends React.Component { static contextTypes = { color: PropTypes.string }; render() { return ( ); } } class Message extends React.Component { render() { return ({this.props.text}); } } class MessageList extends React.Component { static childContextTypes = { color: PropTypes.string }; getChildContext() { return {color: "red"}; } render() { const children = this.props.messages.map((message) =>); return {children}; } }
我們可以看到MessageList通過函數(shù)getChildContext顯式聲明提供color屬性,并且通過靜態(tài)屬性childContextTypes聲明了該屬性的類型。而Button通過靜態(tài)屬性contextTypes聲明了要使用屬性的類型,二者通過協(xié)商的方式約定了跨層級(jí)傳遞屬性的信息。Context確實(shí)非常方便的解決了跨層級(jí)傳遞屬性的情況,但是為什么官方卻不推薦使用呢?
首先Context的使用是與React可復(fù)用組件的邏輯背道而馳的,在React的思維中,所有組件應(yīng)該具有復(fù)用的特性,但是正是因?yàn)镃ontext的引入,組件復(fù)用的使用變得嚴(yán)格起來。就以上面的代碼為例,如果想要復(fù)用Button組件,必須在上層組件中含有一個(gè)可以提供String類型的colorContext,所以復(fù)用要求變得嚴(yán)格起來。并且更重要的是,當(dāng)你嘗試修改Context的值時(shí),可能會(huì)觸發(fā)不確定的狀態(tài)。我們舉一個(gè)例子,我們將上面的MessageList稍作改造,使得Context內(nèi)容可以動(dòng)態(tài)改變:
class MessageList extends React.Component { state = { color: "red" }; static childContextTypes = { color: PropTypes.string }; getChildContext() { return {color: this.state.color}; } render() { const children = this.props.messages.map((message) =>); return ( ); } _changeColor = () => { const colors = ["red", "green", "blue"]; const index = (colors.indexOf(this.state.color) + 1) % 3; this.setState({ color: colors[index] }); } }{children}
上面的例子中我們MessageList組件Context提供的color屬性改成了state的屬性,當(dāng)每次使用setState刷新color的時(shí)候,子組件也會(huì)被刷新,因此對(duì)應(yīng)按鈕的顏色也會(huì)發(fā)生改變,一切看起來是非常的完美。但是一旦組件間的組件存在生命周期函數(shù)ShouldComponentUpdate那么一切就變得詭異起來。我們知道PureComponent實(shí)質(zhì)就是利用ShouldComponentUpdate避免不必要的刷新的,因此我們可以對(duì)之前的例子做一個(gè)小小的改造:
class Message extends React.PureComponent { render() { return ({this.props.text}); } }
你會(huì)發(fā)現(xiàn)即使你在MessageList中改變了Context的值,也無法導(dǎo)致子組件中按鈕的顏色刷新。這是因?yàn)?b>Message組件繼承自PureComponent,在沒有接受到新的props改變或者state變化時(shí)生命周期函數(shù)shouldComponentUpdate返回的是false,因此Message及其子組件并沒有刷新,導(dǎo)致Button組件沒有刷新到最新的顏色。
如果你的Context值是不會(huì)改變的,或者只是在組件初始化的時(shí)候才會(huì)使用一次,那么一切問題都不會(huì)存在。但是如果需要改變Context的情況下,如何安全使用呢? Michel Weststrate在[How to safely use React context
](https://medium.com/@mweststra...。作者認(rèn)為我們不應(yīng)該直接在getChildContext中直接返回state屬性,而是應(yīng)該像依賴注入(DI)一樣使用conext。
class Theme { constructor(color) { this.color = color this.subscriptions = [] } setColor(color) { this.color = color this.subscriptions.forEach(f => f()) } subscribe(f) { this.subscriptions.push(f) } } class Button extends React.Component { static contextTypes = { theme: PropTypes.Object }; componentDidMount() { this.context.theme.subscribe(() => this.forceUpdate()); } render() { return ( ); } } class MessageList extends React.Component { constructor(props){ super(props); this.theme = new Theme("red"); } static childContextTypes = { theme: PropTypes.Object }; getChildContext() { return { theme: this.theme }; } render() { const children = this.props.messages.map((message) =>); return ( ); } _changeColor = () => { const colors = ["red", "green", "blue"]; const index = (colors.indexOf(this.theme.color) + 1) % 3; this.theme.setColor(colors[index]); } }{children}
在上面的例子中我們創(chuàng)造了一個(gè)Theme類用來管理樣式,然后通過Context將Theme的實(shí)例向下傳遞,在Button中獲取到該實(shí)例并且訂閱樣式變化,在樣式變化時(shí)調(diào)用forceUpdate強(qiáng)制刷新達(dá)到刷新界面的目的。當(dāng)然上面的例子只是一個(gè)雛形,具體使用時(shí)還需要考慮到其他的方面內(nèi)容,例如在組件銷毀時(shí)需要取消監(jiān)聽等方面。
回顧一下之前版本的Context,配置起來還是比較麻煩的,尤其還需要在對(duì)應(yīng)的兩個(gè)組件中分別使用childContextTypes和contextTypes的聲明Context屬性的類型。而且其實(shí)這兩個(gè)類型聲明并不能很好的約束context。舉一個(gè)例子,假設(shè)分別有三個(gè)組件: GrandFather、Father、Son,渲染順序分別是:
GrandFather -> Father -> Son
那么假設(shè)說組件GrandFather提供的context是類型為number鍵為value的值1,而Father提供也是類型為number的鍵為value的值2,組件Son聲明獲得的是類型為number的鍵為value的context,我們肯定知道組件Son中this.context.value值為2,因?yàn)閏ontext在遇到同名Key值時(shí)肯定取的是最靠近的父組件。
同樣地我們假設(shè)件GrandFather提供的context是類型為string鍵為value的值"1",而Father提供是類型為number的鍵為value的值2,組件Son聲明獲得的是類型為string的鍵為value的context,那么組件Son會(huì)取到GrandFather的context值嗎?事實(shí)上并不會(huì),仍然取到的值是2,只不過在開發(fā)過程環(huán)境下會(huì)輸出:
Invalid context value of type number supplied to Son, expected string
因此我們能得出靜態(tài)屬性childContextTypes和contextTypes只能提供開發(fā)的輔助性作用,對(duì)實(shí)際的context取值并不能起到約束性的作用,即使這樣我們也不得不重復(fù)體力勞動(dòng),一遍遍的聲明childContextTypes和contextTypes屬性。
New Context新的Context發(fā)布于React 16.3版本,相比于之前組件內(nèi)部協(xié)商聲明的方式,新版本下的Context大不相同,采用了聲明式的寫法,通過render props的方式獲取Context,不會(huì)受到生命周期shouldComponentUpdate的影響。上面的例子用新的Context改寫為:
import React, {Component} from "react"; const ThemeContext = React.createContext({ theme: "red"}); class Button extends React.Component { render(){ return({({color}) => { return ( ); }} ); } } class Message extends React.PureComponent { render() { return ({this.props.text}); } } class MessageList extends React.Component { state = { theme: { color: "red" } }; render() { return () } _changeColor = () => { const colors = ["red", "green", "blue"]; const index = (colors.indexOf(this.state.theme.color) + 1) % 3; this.setState({ theme: { color: colors[index] } }); } } {this.props.messages.map((message) =>)}
我們可以看到新的Context使用React.createContext的方式創(chuàng)建了一個(gè)Context實(shí)例,然后通過Provider的方式提供Context值,而通過Consumer配合render props的方式獲取到Context值,即使中間組件中存在shouldComponentUpdate返回false,也不會(huì)導(dǎo)致Context無法刷新的問題,解決了之前存在的問題。我們看到在調(diào)用React.createContext創(chuàng)建Context實(shí)例的時(shí)候,我們傳入了一個(gè)默認(rèn)的Context值,該值僅會(huì)在Consumer在組件樹中無法找到匹配的Provider才會(huì)使用,因此即使你給Provider的value傳入undefined值時(shí),Consumer也不會(huì)使用默認(rèn)值。
新版的Context API相比于之前的Context API更符合React的思想,并且能解決componentShouldUpdate的帶來的問題。與此同時(shí)你的項(xiàng)目需要增加專門的文件來創(chuàng)建Context。在 React v17 中,可能就會(huì)刪除對(duì)老版 Context API 的支持,所以還是需要盡快升級(jí)。最后講了這么多,但是在項(xiàng)目中還是要盡量避免Context的濫用,否則會(huì)造成組件間依賴過于復(fù)雜。
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.specialneedsforspecialkids.com/yun/101146.html
摘要:然而之前的相當(dāng)于從最頂層的組件開始,自頂向下遞歸調(diào)用,不會(huì)被中斷,這樣就會(huì)持續(xù)占用瀏覽器主線程。眾所周知,是單線程運(yùn)行,長(zhǎng)時(shí)間占用主線程會(huì)阻塞其他類似于樣式計(jì)算布局繪制等運(yùn)算,從而出現(xiàn)掉幀的情況。 前言 首先歡迎大家關(guān)注我的Github博客,也算是對(duì)我的一點(diǎn)鼓勵(lì),畢竟寫東西沒法獲得變現(xiàn),能堅(jiān)持下去也是靠的是自己的熱情和大家的鼓勵(lì),希望大家多多關(guān)注呀!從今年年初離開React開發(fā)崗,...
摘要:前言我們知道在使用時(shí),我們需要通過去創(chuàng)建實(shí)例,譬如為的配置文件那么我們看下方法的具體實(shí)現(xiàn)創(chuàng)建實(shí)例并執(zhí)行解析主要通過執(zhí)行對(duì)配置文件的解析,具體實(shí)現(xiàn)如下文配置文件解析解析標(biāo)簽解析標(biāo)簽解析別名標(biāo)簽解析插件標(biāo)簽解析標(biāo)簽解析標(biāo)簽解析標(biāo)簽從的方法實(shí)現(xiàn)我 前言 我們知道在使用 Mybatis 時(shí),我們需要通過 SqlSessionFactoryBuild 去創(chuàng)建 SqlSessionFactory ...
摘要:為何重拾使用了多年,但是對(duì)其底層的一些實(shí)現(xiàn)還是一知半解,一些概念比較模糊故決定重新拾起,加深對(duì)的認(rèn)識(shí)。小結(jié)是在完成創(chuàng)建后對(duì)其進(jìn)行后置處理的接口是在完成實(shí)例化對(duì)其進(jìn)行的后置處理接口是框架底層的核心接口,其提供了創(chuàng)建,獲取等核心功能。 為何重拾 使用了 Spring 多年,但是對(duì)其底層的一些實(shí)現(xiàn)還是一知半解,一些概念比較模糊;故決定重新拾起,加深對(duì) Spring 的認(rèn)識(shí)。 重拾計(jì)劃 spr...
摘要:目錄結(jié)構(gòu)說明集多編程范式之大成者,使開發(fā)者能夠快速的開發(fā)測(cè)試部署程序,支持全平臺(tái)靜態(tài)編譯。上目錄位置主要目錄包含如下圖,分別進(jìn)行說明文件夾存放檢查器的輔助文件。工作區(qū)有個(gè)子目錄目錄目錄和目錄。目錄用于以代碼包的形式組織并保存源碼文件。 go 目錄結(jié)構(gòu)說明 ??golang集多編程范式之大成者,使開發(fā)者能夠快速的開發(fā)、測(cè)試、部署程序,支持全平臺(tái)靜態(tài)編譯。go具有優(yōu)秀的依賴管理,高效的運(yùn)行...
閱讀 3091·2021-10-12 10:20
閱讀 2824·2021-09-27 13:56
閱讀 799·2021-09-27 13:36
閱讀 1439·2021-09-26 09:46
閱讀 2425·2019-08-30 14:02
閱讀 2693·2019-08-28 18:14
閱讀 1270·2019-08-26 10:32
閱讀 1712·2019-08-23 18:25