JobbyM's Blog

react Context

此文章是翻译Context这篇React(版本v15.4.0)官方文档。

Context

在React 中,很容易追踪数据通过你的React components 的流动。但你察看一个comopnent 时,你可以看到那一个props 被传入,这使得你的应用很容易推理。

在一些情况下,你希望通过传递数据给component 树,而不需要在每一级手动通过props 进行传递。你可以在React 中直接使用强大的“context”API。

Why Not To Use Context

绝大多数应用不需要使用context。

如果你想要你的应用是健壮的,就不能使用context。这是一个实验性API并且它很可能在未来React 的发布版本中取消。

如果你不熟悉像ReduxMobX 这种state 管理库,不要使用context。在许多实际应用中,这些库以及和React 绑定是一个很好的管理和许多components 相关的state。更有可能的是Redux 和context 相比是一个很好的解决方案。

如果你不是一个 经验丰富的React 开发者,不要使用context。这通常有只需要使用props 和state 就能实现这些功能。

如果你坚持使用context 而不管这些警告,尝试将你使用的context 独立在一小块区域中避免直接使用这些context API以防止当这些API 改变后便于升级。

How To Use Context

假设你有一个如下的结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class Button extends Component {
render() {
return (
<button style={{background: this.props.color}}>
{this.props.children}
</button>
);
}
}

class Message extends Component {
render() {
return (
<div>
{this.props.text} <Button color={this.props.color}>Delete</Button>
</div>
);
}
}

class MessageList extends Component {
render() {
const color = "purple";
const children = this.props.messages.map((message) =>
<Message text={message.text} color={color} />
);
return <div>{children}</div>;
}
}

在这个例子中,我们以适当的方式 手动得传入一个color props 到 ButtonMessage components 中。使用context,我们可以通过这个树自动传入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class Button extends Component {
render() {
return (
<button style={{background: this.context.color}}>
{this.props.children}
</button>
);
}
}

Button.contextTypes = {
color: React.PropTypes.string
}

class Message extends Component {
render() {
return (
<div>
{this.props.text} <Button>Delete</Button>
</div>
);
}
}

class MessageList extends Component {
getChildContext(){
return {color: "purple"}
}
render() {
const color = "purple";
const children = this.props.messages.map((message) =>
<Message text={message.text} />
);
return <div>{children}</div>;
}
}

MessageList.childContextTypes = {
color: React.PropTypes.string
}
`

通过添加childContextTypesgetChildContextMessageList(context 提供者),React 可以自动向下传递信息并且子树中的任何components (这个例子中的Button)都可以通过定义contextTypes 去访问它。

如果contextTypes 没有被定义,那么这个context 将是一个空对象。

Parent-Child Coupling

Context 也运行你创建一个父子通信的API。例如,React Router V4 就是使用这种方式的一个库:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const BasicExample = () => (
<Router>
<div>
<ul>
<li><Link to="/">Home</Link></li>
<li><Link to="/about">About</Link></li>
<li><Link to="/topics">Topics</Link></li>
</ul>

<hr/>

<Match exactly pattern="/" component={Home} />
<Match pattern="/about" component={About} />
<Match pattern="/topics" component={Topics} />
</div>
</Router>
)

Router componet 向下传递一些信息,每一个LinkMatch 都可以沟通回到到包含容器Router

在你使用类似的API 构建components 之前,考虑是否有一个更加的替代方案。例如,如果你喜欢你可以将整个React component 作为props 传入。

Referencing Context in Lifecycle Methods

如果contextTypes 在component 中被定义,下面这些lifecycle methods 将会接受一个context 对象作为参数:

Referencing Context in Stateless Functional Components

无状态的功能性components 也可以引用context 如果contextTypes 作为函数的属性被定义。下面代码展示一个无状态的功能性 Button component 。

1
2
3
4
5
6
const Button = ({children}, context) =>
<button style={{background: context.color}}>
{children}
</button>

Button.contextTypes = {color: React.PropTypes.string}

Updating Context

不要做它。

React 有一个可以更新context 的API,但是它会从根本上造成破坏,所以你不应该使用它。

当state 和props 改变时,就会调用getChildContext 函数。为了更新context 中的数据,使用this.setState 触发本地(local)state 更新。这将会产生新的context 并且改变会被子节点收到。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
class MediaQuery extends Component {
constructor(props)
this.state = {
type: 'desktop'
}

getChildContext(){
return {
type: this.state.type
}
}

componentDidMount(){
const checkMediaQuery = () => {
const type = window.matchMedia("(min-width: 1025px)").matches ? 'desktop' : 'mobile'
if(type !== this.state.type){
this.setState({type})
}
}

window.addEventListener('resize', checkMediaQuery)
checkMediaQuery()
}

render(){
return this.props.children
}
}

MediaQuery.childContextTypes = {
type: React.PropTypes.string
}

问题是,如果component 提供context 反生改变,后代使用这个值将不会更新如果中间件的父的shouldComponentUpdate 返回false。使用context 这个component 是完全失去控制,所以这里基本没有办法依赖更新context。这篇文章 解释了为什么这是一个问题并且你怎幺避免它。

参考文档

  1. Context