React Context用法小結(附完整代碼)
前言
傳統的React應用中,數據通過props屬性自上而下(由父組件向子組件)傳遞,當組件層級數量增多時,在每一層傳遞props則很繁瑣,Context提供瞭一種新的組件之間共享數據的方式,允許數據隔代傳遞,而不必顯式的通過組件樹逐層傳遞props。
根據官網鏈接對Context常用的5種使用場景進行瞭整理,並隨文配上完整代碼。
使用場景:
- 父組件使用Provider生產數據,子組件使用Consumer消費數據
- 子組件使用ContextType接收數據
- 動態和靜態Context(父組件更新Context,被Provider包裹的子組件刷新數據,沒被Provider包裹的子組件使用Context默認值)
- 在嵌套組件中更新Context(子組件通過Context傳遞的函數更新數據)
- 消費多個Context
知識點匯總
- 首先創建1個Context對象,當React渲染1個訂閱瞭Context對象的組件,這個組件會從組件樹中離自身最近的那個匹配的Provider讀取到當前的context值。
- 每個Context都返回1個React組件,允許消費組件訂閱context的變化
- ContextType隻能在類組件中使用
- 1個組件如果有多個消費者,ContextType支隊其中1個有效,也就是ContextType隻能有1個(所以推薦使用Provider和Consumer形式)
場景1:使用Provider和Consumer生產和消費數據 文件目錄及說明
以下文件在同級路徑:
- ProductContext.js:創建的Context組件文件
- ProviderPage.js:父組件(數據生產者)
- MiddlePage.js:中間組件
- ConsumerPage.js:子組件(數據消費者)
數據傳遞路徑:
ProviderPage.js ==> MiddlePage.js ==> ConsumerPage.js
代碼文件 ProductContext.js:
import React from 'react'; // Context 可以讓我們無須明確地傳遍每一個組件,就能將值深入傳遞進組件樹。 // 創建1個Context對象並給定默認值,如果沒有匹配到Provider,消費組件取Context默認值 export const ProductContext = React.createContext({ name: 'car', price: 8000, unit: '$', }); export const { Provider, Consumer } = ProductContext;
ProviderPage.js:
import React, { PureComponent } from 'react'; import MiddlePage from './MiddlePage'; import { Provider } from './ProductContext'; class ProviderPage extends PureComponent { state = { product: { name: 'plane', price: 120000, unit: '$', }, }; render() { const { product } = this.state; return ( <div> <h1>根組件使用Provider傳值,子組件使用Consumer接收</h1> <Provider value={product}> <MiddlePage /> </Provider> {/*不用Provider,顯示Context對象defaultValue*/} {/* <MiddlePage />*/} </div> ); } } export default ProviderPage;
MiddlePage.js:
import React, { PureComponent } from 'react'; import ConsumerPage from './ConsumerPage'; class MiddlePage extends PureComponent { render() { return ( <ConsumerPage /> ); } } export default MiddlePage;
ConsumerPage.js:
import React, { PureComponent } from 'react'; import { Consumer } from './ProductContext'; class ConsumerPage extends PureComponent { render() { return ( <Consumer> {context => { return ( <div> name:{context.name} <br /> price:{context.price} <br /> unit:{context.unit} </div> ); }} </Consumer> ); } } export default ConsumerPage;
效果
可以看到顯示的是ProviderPage組件提供的Context值。
場景2:使用ContextType接收數據
文件目錄及說明
- FamilyContext.js:創建的Context組件
- FatherPage.js:父組件(生產數據)
- SonPage.js:子組件(中間組件)
- GrandSonPage.js:孫組件(消費數據)
代碼文件
FamilyContext.js
import React, { PureComponent } from 'react'; import SonPage from './SonPage'; import { Provider } from './FamilyContext'; class FatherPage extends PureComponent { state = { person: { name: 'Lora', age: 99, gender: 'female', }, }; render() { const { person } = this.state; return ( <div> <h1>使用ContextType消費Context數據</h1> <Provider value={person}> <SonPage /> </Provider> {/*不用Provider,顯示Context對象defaultValue*/} {/*<SonPage />*/} </div> ); } } export default FatherPage;
SonPage.js:和上面的MiddlePage.js一樣
import React, { PureComponent } from 'react'; import GrandSonPage from './GrandSonPage' class SonPage extends PureComponent { render() { return ( <GrandSonPage /> ); } } export default SonPage;
GrandSonPage.js:使用ContextType
接收數據
import React, { PureComponent } from 'react'; import { FamilyContext } from './FamilyContext'; class GrandSonPage extends PureComponent { // static contextType = FamilyContext; componentDidMount() { // 使用contexType可以在任意生命周期訪問數據 // 使用 this.context 來消費最近 Context 上的那個值 const value = this.context; console.log(value); } render() { // Context是1個對象,對對象進行解構操作 const { name, age, gender } = this.context; return ( <div> name:{name} <br /> age:{age} <br /> gender:{gender} </div> ); } } export default GrandSonPage;
效果
場景3:動態和靜態Context
在ProviderPage.js中,被Provider
包裹的組件可以更新的Context數據,沒被Provider
包裹的組件隻能獲取Context默認值。
代碼文件
ProductContext.js
import React from 'react'; // Context 可以讓我們無須明確地傳遍每一個組件,就能將值深入傳遞進組件樹。 // 創建1個Context對象 // 默認值,如果沒有匹配到Provider取默認值 export const ProductContext = React.createContext({ name: 'car', price: 17000, unit: '$', }); export const { Provider, Consumer } = ProductContext;
ProviderPage.js
import React, { PureComponent } from 'react'; import { Button, Divider } from 'antd'; import MiddlePage from './MiddlePage'; import { Provider } from './ProductContext'; class ProviderPage extends PureComponent { state = { product: { name: 'plane', price: 120000, unit: '$', }, }; handleChange = () => { const { product: { name, price, unit } } = this.state; this.setState({ product: { name, price: price + 1, unit, }, }); }; render() { const { product } = this.state; return ( <div> <h1>父組件更新Context,被Provider包裹的子組件刷新值,沒被包裹的子組件使用Context默認值</h1> {/*在Provider包裹的內部組件使用state中的值*/} <Provider value={product}> <MiddlePage /> </Provider> <Divider /> {/*不在Provider包裹的外部組件使用ProductContext重的默認值*/} <MiddlePage /> <Divider /> <Button onClick={this.handleChange} type="primary" > 增加 </Button> </div> // {不用Provider,顯示Context對象defaultValue // <MiddlePage /> ); } } export default ProviderPage;
MiddlePage.js
import React, { PureComponent } from 'react'; import ConsumerPage from './ConsumerPage'; class MiddlePage extends PureComponent { render() { return ( <ConsumerPage /> ); } } export default MiddlePage;
ConsumerPage.js
import React, { PureComponent } from 'react'; import { Consumer } from './ProductContext'; class ConsumerPage extends PureComponent { render() { return ( <Consumer> {context => { return ( <div> name:{context.name} <br /> price:{context.price} <br /> unit:{context.unit} </div> ); }} </Consumer> ); } } export default ConsumerPage;
效果
點擊增加按鈕,上面的price會增加(使用Context更新值),下面的不變(使用Context默認值)。
場景4:在嵌套組件中更新Context
代碼文件
ProductContext.js
import React from 'react'; // Context 可以讓我們無須明確地傳遍每一個組件,就能將值深入傳遞進組件樹。 // 創建1個Context對象 // 註意第1個參數是1個object {} export const ProductContext = React.createContext({ product: { name: 'car', price: 8000, unit: '$', }, // 通過context傳遞1個函數,使得consumer組件更新context handlePrice: () => { }, }, ); export const { Provider, Consumer } = ProductContext;
ProviderPage.js
import React, { PureComponent } from 'react'; import MiddlePage from './MiddlePage'; import { Provider } from './ProductContext'; class ProviderPage extends PureComponent { state = { product: { name: 'plane', price: 120000, unit: '$', }, // state頁包含瞭更新函數,因此會被傳遞進context provider handlePrice: () => this.handlePrice(), }; handlePrice = () => { const { product: { name, price, unit } } = this.state; this.setState({ product: { name, price: price + 1, unit, }, }); }; render() { const { product, handlePrice } = this.state; return ( <div> <h1>子組件通過context傳遞的函數更新context,等於在子組件中更新狀態</h1> {/*註意此時傳遞進去的是state,包含product對象和1個函數*/} <Provider value={{ product, handlePrice }}> <MiddlePage /> </Provider> {/*不用Provider,顯示Context對象defaultValue*/} {/* <MiddlePage />*/} </div> ); } } export default ProviderPage;
MiddlePage.js
import React, { PureComponent } from 'react'; import ConsumerPage from './ConsumerPage'; class MiddlePage extends PureComponent { render() { return ( <ConsumerPage /> ); } } export default MiddlePage;
ConsumerPage.js
import React, { PureComponent } from 'react'; import { Button, Divider } from 'antd'; import { Consumer } from './ProductContext'; class ConsumerPage extends PureComponent { render() { return ( <Consumer> {/*創建的Context對象*/} {/*ConsumerPage組件不僅從context獲取product對象值,還獲取1個handlePrice函數*/} {/*註意參數名要和Provider中傳入的以及context中定義的一摸一樣*/} {context => { return ( <div> name:{context.product.name} <br /> price:{context.product.price} <br /> unit:{context.product.unit} <Divider /> <Button onClick={context.handlePrice} type="primary" > 增加 </Button> </div> ); }} </Consumer> ); } } export default ConsumerPage;
效果
增加按鈕在子組件ConsumerPage.js中定義,Context傳遞1個函數給子組件,具體的函數實現在父組件ProviderPage.js中,此處感覺還是類似React回調函數,優勢就是中間組件MiddlePage不用傳遞任何props,包括回調函數。
場景5:消費多個Context
代碼文件
MultiContext.js
import React from 'react'; const SchoolContext = React.createContext({ name: '南師附中', location: '南京', }); const StudentContext = React.createContext({ name: 'chengzhu', age: 17, }); export { SchoolContext, StudentContext };
ProviderPage.js
import React, { PureComponent } from 'react'; import MiddlePage from './MiddlePage'; import { SchoolContext, StudentContext } from './MultiContext'; class ProviderPage extends PureComponent { state = { school: { name: '清華大學', location: '北京', }, student: { name: '張雲', age: 22, }, }; render() { const { school, student } = this.state; return ( <div> <h1>消費多個Context</h1> <SchoolContext.Provider value={school}> <StudentContext.Provider value={student}> <MiddlePage /> </StudentContext.Provider> </SchoolContext.Provider> </div> // 不用Provider包裹顯示Context中定義的默認值 // <MiddlePage /> ); } } export default ProviderPage;
MiddlePage.js
import React, { PureComponent } from 'react'; import ConsumerPage from './ConsumerPage'; class MiddlePage extends PureComponent { render() { return ( <ConsumerPage /> ); } } export default MiddlePage;
ConsumerPage.js
import React, { PureComponent } from 'react'; import { SchoolContext, StudentContext } from './MultiContext'; class ConsumerPage extends PureComponent { render() { return ( <SchoolContext.Consumer> {school => ( <StudentContext.Consumer> {student => { return ( <div> 就讀學校: {school.name} <br /> 學校位置: {school.location} <br /> 學生姓名: {student.name} <br /> 學生年齡: {student.age} </div> ); }} </StudentContext.Consumer> )} </SchoolContext.Consumer> ); } } export default ConsumerPage;
效果
可以看到傳遞瞭2個Context,分別是SchoolContext和StudentContext,子組件ConsumerPage消費瞭傳遞進來的2個Context數據。
到此這篇關於React Context用法小結(附完整代碼)的文章就介紹到這瞭,更多相關React Context用法內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!