React组件树A节点,用Provider提供者注入了theme,然后在需要theme的地方,用Consumer消费者形式取出theme,提供给组件渲染使用。

需要注意的是,提供者永远都需要在消费者上层,正所谓水往低处流,提供者一定要是消费者的某一层父级

  • 老版本的Context

老版本中,React用PropTypes来声明context类型,提供者需要getChildContext来返回需要提供的context,并且用静态属性childContextTypes声明所需要提供的context数据类型。

提供者:

import propsTypes from 'proptypes'

class ProviderDemo extends React.Component {
getChildContext(){
const theme = {
color: '#ccc',
background: 'pink'
}
return {theme}
}
render () {
return <div>
hello,let us learn react!
<Son></Son>
</div>
}
}

ProviderDemo.childContextTypes = {
theme: propsTypes.object
}


在老版本中,需要通过getChildContext方法,将传递的theme信息返回出去,并通过childContextTypes 声明要传递的theme是一个对象结构。声明类型需要用propsTypes库来助力


消费者:

// 消费者
class ConsumerDemo extends React.Component{
render () {
console.log(this.context.theme)
const {color,background} = this.context.theme
return <div style={{color,backgaound}}>消费者</div>
}
}

ConsumerDemo.contextTypes = {
theme: propsTypes.object
}

const Son = () => <ConsumerDemo></ConsumerDemo>


作为消费者,需要在组件的静态属性指明我到底需要哪个提供者提供的状态,在demo项目中,ConsumerDemo的contextTyps明确的指明了需要ProviderDemo提供的theme信息,然后就可以通过this.context.theme访问到theme,用来做渲染消费。


  • 新版本的context

最新版中,我们可以直接使用createContext创建出一个context上下文对象,context对象提供两个组件,Providewr和Consumer作为新的提供者和消费者,这种context模式,更便捷的传递context。

  1. createContext

react.createContext 的基本用法如下


const ThemeContext = React.createContext(null) // 创建context const ThemeProvider = themeContext.Provider // 提供者 const ThemeConsumer = ThemeContext.Consumer // 消费者


createContext接受一个参数,作为初始化context的内容,返回一个context对象,Context对象上的Provider作为提供者,Context对象上的Consumer作为消费者。

  1. 新版本的提供者-Provider用法
const ThemeProvider = ThemeContext.Provider// 提供者

export default function ProviderDemo(){
const [ contextValue , setContextValue ] = React.useState({ color:'#ccc', background:'pink' })
return <div>
<ThemeProvider value={ contextValue } >
<Son />
</ThemeProvider>
</div>
}

Provider 的作用有两个。

a。value属性传递context,提供给消费者使用。

b。value属性改变,themeProvider 会让消费Provider value的组件重新渲染。

  1. 新版消费者

在新版中消费者获取context,提供了三种方式。

  • 类组件中的 contextType方式。
const ThemeContext = React.createContext(null)
// 类组件 - contextType 方式
class ConsumerDemo extends React.Component{
render(){
const { color,background } = this.context
return <div style={{ color,background } } >消费者</div>
}
}
ConsumerDemo.contextType = ThemeContext

const Son = ()=> <ConsumerDemo />
  1. 类组件的静态属性上的contextType属性,指向需要获取的context,就可以方便获取到最近一层Provider提供的contextValue的值。
  2. 记住这种方式只适用于类组件。

函数组件 useContext方式

在函数组件中,我们使用useContext 来获取上下文。

const ThemeContext = React.createContext(null)
// 函数组件 - useContext方式
function ConsumerDemo(){
const contextValue = React.useContext(ThemeContext) /* */
const { color,background } = contextValue
return <div style={{ color,background } } >消费者</div>
}
const Son = ()=> <ConsumerDemo />

useContext 接受一个参数,就是想要获取的 context ,返回一个 value 值,就是最近的 provider 提供 contextValue 值。

  • 订阅者之 Consumer 方式
  • React之Context 上下文模式_静态属性


这种方式,订阅者采用 render props 方式,接受最近一层 provider中的value属性,作为render props函数的参数,可以将参数取出,作为props 混入 consumerDemo组件,说白了就是context 变成了props。

  • 动态的context
function ConsumerDemo(){
const { color,background } = React.useContext(ThemeContext)
return <div style={{ color,background } } >消费者</div>
}
const Son = React.memo(()=> <ConsumerDemo />) // 子组件

const ThemeProvider = ThemeContext.Provider //提供者
export default function ProviderDemo(){
const [ contextValue , setContextValue ] = React.useState({ color:'#ccc', background:'pink' })
return <div>
<ThemeProvider value={ contextValue } >
<Son />
</ThemeProvider>
<button onClick={ ()=> setContextValue({ color:'#fff' , background:'blue' }) } >切换主题</button>
</div>
}
  • 嵌套Provider
const ThemeContext = React.createContext(null) // 主题颜色Context
const LanContext = React.createContext(null) // 主题语言Context

function ConsumerDemo(){
return <ThemeContext.Consumer>
{ (themeContextValue)=> (
<LanContext.Consumer>
{ (lanContextValue) => {
const { color , background } = themeContextValue
return <div style={{ color,background } } > { lanContextValue === 'CH' ? '大家好,让我们一起学习React!' : 'Hello, let us learn React!' } </div>
} }
</LanContext.Consumer>
) }
</ThemeContext.Consumer>
}

const Son = memo(()=> <ConsumerDemo />)
export default function ProviderDemo(){
const [ themeContextValue ] = React.useState({ color:'#FFF', background:'blue' })
const [ lanContextValue ] = React.useState('CH') // CH -> 中文 , EN -> 英文
return <ThemeContext.Provider value={themeContextValue} >
<LanContext.Provider value={lanContextValue} >
<Son />
</LanContext.Provider>
</ThemeContext.Provider>
}
  • 逐层传递Provider
// 逐层传递Provder
const ThemeContext = React.createContext(null)
function Son2(){
return <ThemeContext.Consumer>
{ (themeContextValue2)=>{
const { color , background } = themeContextValue2
return <div className="sonbox" style={{ color,background } } > 第二层Provder </div>
} }
</ThemeContext.Consumer>
}
function Son(){
const { color, background } = React.useContext(ThemeContext)
const [ themeContextValue2 ] = React.useState({ color:'#fff', background:'blue' })
/* 第二层 Provder 传递内容 */
return <div className='box' style={{ color,background } } >
第一层Provder
<ThemeContext.Provider value={ themeContextValue2 } >
<Son2 />
</ThemeContext.Provider>
</div>

}

export default function Provider1Demo(){
const [ themeContextValue ] = React.useState({ color:'orange', background:'pink' })
/* 第一层 Provider 传递内容 */
return <ThemeContext.Provider value={ themeContextValue } >
<Son/>
</ThemeContext.Provider>
}