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