摘要:官方對(duì)的介紹是意思就是提供了一種通過組件樹傳遞數(shù)據(jù)的方法,而無(wú)需在每個(gè)級(jí)別手動(dòng)傳遞。這也是基于重要物證哈哈實(shí)例使用學(xué)習(xí)技術(shù)最終是要有產(chǎn)出的。依然被視作一個(gè)組件,不過不同的是它的子組件必須是一個(gè)方法并且該方法接收當(dāng)前對(duì)象并最終返回一個(gè)節(jié)點(diǎn)。
拋轉(zhuǎn)引玉
通過上一篇的科普我們知道如果父節(jié)點(diǎn)需要向子節(jié)點(diǎn)傳遞數(shù)據(jù),那么就得通過Props來(lái)實(shí)現(xiàn);那么擺在我們眼前的就有一個(gè)問題了:現(xiàn)有N個(gè)節(jié)點(diǎn)并且它們都是嵌套成父子結(jié)構(gòu),大致如下
......
如過last組件需要A組件的某個(gè)數(shù)據(jù),按照之前的說(shuō)法我們可以使用Props;但是我覺得一般人都不會(huì)這么做,為什么?一個(gè)數(shù)據(jù)在N個(gè)組件中通過Props傳遞,首先寫法上會(huì)很榮譽(yù)、其次就是很可能在某個(gè)節(jié)點(diǎn)寫錯(cuò)了造成最終拿到的數(shù)據(jù)不是想要的數(shù)據(jù),這些都是我們需要考慮的問題。當(dāng)然有人會(huì)想到使用Redux或者Mobx這種第三方庫(kù)來(lái)解決,沒毛??;但如果只是一個(gè)小小的需求就引入了一個(gè)庫(kù),是不是殺雞用了牛刀?在這個(gè)問題上React本身有自己的解決方案:Context。
Context是什么?目前React的Context API已經(jīng)出了兩版,在React16.3.0版本之前和之后。實(shí)際上我們開發(fā)React項(xiàng)目時(shí)候很少會(huì)用到這個(gè)API(至少小編身邊是這種情況);而且對(duì)于第一版的Context就連官方也不建議用,首先是不好用其次是問題多,不過即使如此不堪的技術(shù)卻是Redux的基礎(chǔ)技術(shù),真的是厲害了!
后來(lái)在React16.3.0版本更新之后,全新的Context API與我們見面,可以說(shuō)是脫胎換骨。官方對(duì)Context的介紹是:
Context provides a way to pass data through the component tree without having to pass props down manually at every level.
意思就是Context提供了一種通過組件樹傳遞數(shù)據(jù)的方法,而無(wú)需在每個(gè)級(jí)別手動(dòng)傳遞props??梢钥闯鲞@個(gè)技術(shù)剛好可以用來(lái)解決我們前面提出的問題。
Context可以做什么?事實(shí)上官方設(shè)計(jì)這個(gè)API的目的是共享可被視為React組件樹的“全局”數(shù)據(jù),例如當(dāng)前經(jīng)過身份驗(yàn)證的用戶,主題或首選語(yǔ)言。意圖言簡(jiǎn)意賅,可以理解成為React組件樹(從Root節(jié)點(diǎn)開始通過不斷組合各個(gè)組件形成最終的樹形結(jié)構(gòu))中注入了一個(gè)上下文對(duì)象同時(shí)將一些全局通用的數(shù)據(jù)放在這個(gè)對(duì)象中,這樣我們就可以在這個(gè)組件樹的任何地方使用這些數(shù)據(jù)。
如何使用Context?針對(duì)新版Context,官方給我們提供了三個(gè)API:
React.createContext
Provider
Consumer
通過字面意思大家應(yīng)該就能猜到它們分別的作用了吧!
React.createContext: 用來(lái)創(chuàng)建Context對(duì)象
Provider: 用來(lái)向組件樹發(fā)出Context對(duì)象
Consumer: 使用Context對(duì)象
不過呢,后兩者其實(shí)是React.createContext創(chuàng)建出來(lái)的對(duì)象的組成,用一段代碼來(lái)解釋吧:
const {Provider, Consumer} = React.createContext(defaultValue);
嗯...就醬紫?。。?!
其實(shí)寫到這里我相信用過Redux的朋友就已經(jīng)開始覺得眼熟了,就是Provider和createContext。因?yàn)?b>react-redux提供Provider, Redux提供createStore。這也是Redux基于Context API重要物證哈哈....
學(xué)習(xí)技術(shù)最終是要有產(chǎn)出的。筆者也一步一步來(lái)實(shí)現(xiàn)一個(gè)簡(jiǎn)單例子,功能:通過點(diǎn)擊按鈕對(duì)屏幕中數(shù)字進(jìn)行加1操作
首先我們需要?jiǎng)?chuàng)建兩個(gè)js文件:
buildContext.js
import {createContext} from "react"; const defaultData = {}; export const {Provider, Consumer} = createContext(defaultData);
這里可能有人會(huì)有疑問:為什么將創(chuàng)建Context多帶帶抽離出來(lái)?
1) 將Context和組件隔離;因?yàn)樗鼈儾淮嬖诒匾穆?lián)系,Context只是單純的注入組件而已。
2) 因?yàn)镻rovider, Consumer需要配對(duì)使用(注意:Provider, Consumer配對(duì)使用的前提是它們都來(lái)自同一個(gè)createContext);我們可以在Provider下的任意節(jié)點(diǎn)使用Consumer,所以就可能存在Provider, Consumer不在同一個(gè)組件的情況,所以將將創(chuàng)建Context多帶帶抽離出來(lái)使得處理Context更加優(yōu)雅。
ContextDemo.js
import React, {Component} from "react" import {Provider, Consumer} from "./buildContext"; class ContextDemo extends Component { constructor(props) { super(props); this.state = { count: 0 }; } addOne = () => { this.setState((preState) => ({ count: preState.count + 1 } ) ) }; render() { return () } } export default ContextDemo{ (context) => {context.count}
}
這里我們重點(diǎn)解釋下Provider與Consumer:
Provider
被視作一個(gè)React組件,它的作用就是接收一個(gè)value屬性并把它當(dāng)做最終Context實(shí)體注入到Provider的所有子組件中;同時(shí)Provider允許被Consumer訂閱一個(gè)或多個(gè)Context的變動(dòng),也就是說(shuō)Provider內(nèi)部可以有N個(gè)Consumer并且它們都可以拿到最新&&相同的Context對(duì)象。
如例子所示,我們將組件的State對(duì)象注入到Provider字組件中,如果State發(fā)生變化那么Provider中的Context對(duì)象必定會(huì)同步發(fā)生變化。
Consumer
依然被視作一個(gè)React組件,不過不同的是它的子組件必須是一個(gè)方法并且該方法接收當(dāng)前Context對(duì)象并最終返回一個(gè)React節(jié)點(diǎn)。同時(shí)這里有兩個(gè)問題需要重點(diǎn)關(guān)注:Consumer接收到的Context對(duì)象為離它最近的那個(gè)Provider注入的Context對(duì)象(且必須是通過value屬性)。因?yàn)镻rovider作為一個(gè)組件也可以進(jìn)行嵌套。不過筆者認(rèn)為多帶帶一個(gè)React項(xiàng)目最好只存在一個(gè)Context對(duì)象而且應(yīng)該作為一個(gè)App級(jí)的Context對(duì)象(也就是將項(xiàng)目的根節(jié)點(diǎn)作為Provider的子組件)。這樣做筆者認(rèn)為有兩個(gè)好處:1)全局只有一個(gè)Context更有利于方便使用和管理;2)作為一個(gè)App級(jí)的Context對(duì)象可以讓我們?cè)陧?xiàng)目的任何一個(gè)地方使用到Context對(duì)象,發(fā)揮Context最大的力量。
如果Provider不存在(如果存在那么必須要有value屬性,否則報(bào)錯(cuò)),那么Consumer獲取到的Context對(duì)象為最初createContext方法的默認(rèn)參數(shù)。
綜上所述:Provider的value == Consumer子組件(function)的入?yún)?/b>
當(dāng)我們理解了這兩個(gè)概念,我們?cè)倩剡^頭來(lái)看代碼;
我們將組件的State(this.state)通過Provider注入到其子組件中,其實(shí)可以預(yù)料到當(dāng)我們更改State時(shí)候Context對(duì)象也會(huì)同步變化最終保持一致。所以:
{ (context) => {context.count}
}
此時(shí)Consumer的子組件(function)的入?yún)?b>context就可以認(rèn)為是this.state的復(fù)制體,所以可以在方法中獲取到相應(yīng)的數(shù)據(jù)并且在點(diǎn)擊按鈕更改了State后Context也發(fā)生變化,從而實(shí)現(xiàn)UI的重新渲染。
小小的測(cè)試前面有句話說(shuō):Provider, Consumer配對(duì)使用的前提是它們都來(lái)自同一個(gè)createContext。因此筆者針對(duì)這點(diǎn)做了兩個(gè)實(shí)驗(yàn),目的是測(cè)試當(dāng)Provider, Consumer不是來(lái)自同一個(gè)createContext會(huì)出現(xiàn)什么情況。這里新建兩個(gè)文件buildContext.js和ContextTest.js
情況一buildContext.js
import {createContext} from "react"; export const {Provider} = createContext({"name": "Mario"}); export const {Consumer} = createContext({"age": "26"});
ContextTest.js
import React, {Component} from "react"; import {Provider, Consumer} from "./buildContext"; class Context extends Component { render() { return ({/*name*/} ) } } export default Context;{/*age*/} { (context) => ( ) }age: {context.age}
name: {context.name}
運(yùn)行的結(jié)果有點(diǎn)意想不到,Consumer拿到的Context并不是離它最近的Provider提供的,而是創(chuàng)造它的createContext方法的默認(rèn)值,即:export const {Consumer} = createContext({"age": "26"});。再換個(gè)寫法看看!
情況二buildContext.js
import {createContext} from "react"; export const NameContext = createContext({"name": "Mario"}); export const AgeContext = createContext({"age": "26"});
ContextTest.js
import React, {Component} from "react"; import {NameContext, AgeContext} from "./buildContext"; class Context extends Component { render() { return ({/*name*/} ) } } export default Context;;{/*age*/} { (context) => ( ) }age: {context.age}
name: {context.name}
這里我們給Provider提供一個(gè)value屬性;
運(yùn)行結(jié)果與第一種情況相同;
結(jié)論因此我們可以猜測(cè):如果Provider, Consumer不是來(lái)自同一個(gè)createContext,那么Consumer獲取到的Context則是自己的createContext方法的默認(rèn)值,此時(shí)的Provider被視為不存在。
Context的簡(jiǎn)單實(shí)用就介紹到這里,本篇很多地方都來(lái)自筆者的個(gè)人看法,如有理解不當(dāng)煩請(qǐng)丟磚。
同時(shí)筆者也嘗試寫了一個(gè)更具有代表性(純屬筆者意淫)的Context應(yīng)用實(shí)例,將Context、ContextHandler、Component分離出來(lái),實(shí)現(xiàn)更改相鄰組件的樣式。麻雀雖小五臟俱全,請(qǐng)各位朋友多多海涵!!
對(duì)了項(xiàng)目啟動(dòng)腳本是npm install || npm start
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/97894.html
摘要:在線傳遞給的是而不是,返回值即是想要透?jìng)鞯臄?shù)據(jù)了。所以函數(shù)組件在每次渲染的時(shí)候如果有傳遞函數(shù)的話都會(huì)重渲染子組件。在學(xué)會(huì)使用React Hooks之前,可以先看一下相關(guān)原理學(xué)習(xí)React Hooks 前言 在 React 的世界中,有容器組件和 UI 組件之分,在 React Hooks 出現(xiàn)之前,UI 組件我們可以使用函數(shù),無(wú)狀態(tài)組件來(lái)展示 UI,而對(duì)于容器組件,函數(shù)組件就顯得無(wú)能為力,我...
摘要:驗(yàn)證路由所謂的驗(yàn)證路由其實(shí)就是該路由的外層加了一層驗(yàn)證機(jī)制,有授權(quán)的用戶才能進(jìn)入,反之都無(wú)法進(jìn)入。一起學(xué)系列也隨著這篇的結(jié)束而告一段落了。大家一起加油最后再獻(xiàn)上和本篇博文有關(guān)的代碼鏈接和示例頁(yè)面 時(shí)隔那么久,博主終于從睡夢(mèng)中醒來(lái)開始更新博客啦!為自己的勤勞歡呼...(pia pia pia打臉)!本次我們接著上一篇博客繼續(xù)聊React-Router4。上篇我們主要了解了React-Ro...
摘要:所以我們做的事情其實(shí)就是,聲明了一個(gè)狀態(tài)變量,把它的初始值設(shè)為,同時(shí)提供了一個(gè)可以更改的函數(shù)。 你還在為該使用無(wú)狀態(tài)組件(Function)還是有狀態(tài)組件(Class)而煩惱嗎? ——擁有了hooks,你再也不需要寫Class了,你的所有組件都將是Function。 你還在為搞不清使用哪個(gè)生命周期鉤子函數(shù)而日夜難眠嗎? ——擁有了Hooks,生命周期鉤子函數(shù)可以先丟一邊了。 你在還...
閱讀 2179·2023-04-26 00:41
閱讀 1223·2021-09-24 10:34
閱讀 3644·2021-09-23 11:21
閱讀 4496·2021-09-22 15:06
閱讀 1611·2019-08-30 15:55
閱讀 941·2019-08-30 15:54
閱讀 1892·2019-08-30 15:48
閱讀 618·2019-08-29 13:58