Context
被翻译为上下文,在编程领域,这是一个经常会接触到的概念,React中也有。
在React的官方文档中,Context
被归类为高级部分(Advanced),属于React的高级API,但官方并不建议在稳定版的App中使用Context。
The vast majority of applications do not need to use content.
If you want your application to be stable, don't use context. It is an experimental API and it is likely to break in future releases of React.
不过,这并非意味着我们不需要关注Context
。事实上,很多优秀的React组件都通过Context来完成自己的功能,比如react-redux的<Provider />
,就是通过Context
提供一个全局态的store
,拖拽组件react-dnd,通过Context
在组件中分发DOM的Drag和Drop事件,路由组件react-router通过Context
管理路由状态等等。在React组件开发中,如果用好Context
,可以让你的组件变得强大,而且灵活。
今天就想跟大家聊一聊,我在开发当中,所认识到的这个Context
,以及我是如何使用它来进行组件开发的。
注:本文中所有提到的App皆指Web端App。
初识React Context
官方对于Context的定义
React文档官网并未对Context
给出“是什么”的定义,更多是描述使用的Context
的场景,以及如何使用Context
。
官网对于使用Context
的场景是这样描述的:
In Some Cases, you want to pass data through the component tree without having to pass the props down manuallys at every level. you can do this directly in React with the powerful "context" API.
简单说就是,当你不想在组件树中通过逐层传递props
或者state
的方式来传递数据时,可以使用Context
来实现跨层级的组件数据传递。
image
使用props或者state传递数据,数据自顶下流。
image
使用Context
,可以跨越组件进行数据传递。
如何使用Context
如果要Context
发挥作用,需要用到两种组件,一个是Context
生产者(Provider),通常是一个父节点,另外是一个Context
的消费者(Consumer),通常是一个或者多个子节点。所以Context
的使用基于生产者消费者模式。
对于父组件,也就是Context
生产者,需要通过一个静态属性childContextTypes
声明提供给子组件的Context
对象的属性,并实现一个实例getChildContext
方法,返回一个代表Context
的纯对象 (plain object) 。
import React from 'react'import PropTypes from 'prop-types'
class MiddleComponent extends React.Component {
render () {
return <ChildComponent />
}
}
class ParentComponent extends React.Component {
// 声明Context对象属性
static childContextTypes = {
propA: PropTypes.string,
methodA: PropTypes.func
}
// 返回Context对象,方法名是约定好的
getChildContext () {
return {
propA: 'propA',
methodA: () => 'methodA'
}
}
render () {
return <MiddleComponent />
}
}
而对于Context
的消费者,通过如下方式访问父组件提供的Context
。
import React from 'react'import PropTypes from 'prop-types'
class ChildComponent extends React.Component {
// 声明需要使用的Context属性
static contextTypes = {
propA: PropTypes.string
}
render () {
const {
propA,
methodA
} = this.context
console.log(`context.propA = ${propA}`) // context.propA = propA
console.log(`context.methodA = ${methodA}`) // context.methodA = undefined
return ...
}
}
子组件需要通过一个静态属性contextTypes
声明后,才能访问父组件Context
对象的属性,否则,即使属性名没写错,拿到的对象也是undefined
。
对于无状态子组件(Stateless Component),可以通过如下方式访问父组件的Context
import React from 'react'import PropTypes from 'prop-types'
const ChildComponent = (props, context) => {
const {
propA
} = context
console.log(`context.propA = ${propA}`) // context.propA = propA
return ...
}
ChildComponent.contextProps = {
propA: PropTypes.string
}
而在接下来的发行版本中,React对Context
的API做了调整,更加明确了生产者消费者模式的使用方式。
import React from 'react';import ReactDOM from 'react-dom';
const ThemeContext = React.createContext({
background: 'red',
color: 'white'
});
通过静态方法React.createContext()
创建一个Context
对象,这个Context
对象包含两个组件,<Provider />
和<Consumer />
。
class App extends React.Component { render () {
return (
<ThemeContext.Provider value={{background: 'green', color: 'white'}}>
<Header />
</ThemeContext.Provider>
);
}
}
<Provider />
的value
相当于现在的getChildContext()
。
class Header extends React.Component { render () {
return (
<Title>Hello React Context API</Title>
);
}
}
class Title extends React.Component {
render () {
return (
<ThemeContext.Consumer>
{context => (
<h1 style={{background: context.background, color: context.color}}>
{this.props.children}
</h1>
)}
</ThemeContext.Consumer>
);
}
}
<Consumer />
的children
必须是一个函数,通过函数的参数获取<Provider />
提供的Context
。
可见,Context
的新API更加贴近React的风格。
几个可以直接获取Context的地方
实际上,除了实例的context
属性(this.context
),React组件还有很多个地方可以直接访问父组件提供的Context
。比如构造方法:
constructor(props, context)
比如生命周期:
componentWillReceiveProps(nextProps, nextContext)
shouldComponentUpdate(nextProps, nextState, nextContext)
componetWillUpdate(nextProps, nextState, nextContext)
对于面向函数的无状态组件,可以通过函数的参数直接访问组件的Context
。
const StatelessComponent = (props, context) => ( ......
)
还没有评论,来说两句吧...