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。
createContext
react.createContext 的基本用法如下
const ThemeContext = React.createContext(null) // 创建context
const ThemeProvider = themeContext.Provider // 提供者
const ThemeConsumer = ThemeContext.Consumer // 消费者
createContext接受一个参数,作为初始化context的内容,返回一个context对象,Context对象上的Provider作为提供者,Context对象上的Consumer作为消费者。
新版本的提供者-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> }
a。value属性传递context,提供给消费者使用。
b。value属性改变,themeProvider 会让消费Provider value的组件重新渲染。
新版消费者
在新版中消费者获取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 />
- 类组件的静态属性上的contextType属性,指向需要获取的context,就可以方便获取到最近一层Provider提供的contextValue的值。
- 记住这种方式只适用于类组件。
函数组件 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 方式
这种方式,订阅者采用 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> }