React context 丢失问题
文本是为了说清
react context
目前的机制是owner context
而不是我们以为的parent context
机制,并说明这两者的区别。希望能对即将使用context的同学有所帮助.
什么是 context
context是为了解决component之间通信的上下文机制,该api目前并未定稿所以react并没有开放出来。最近有大量需要共享上下文的场景才去了解这个api,然后也成功被绕了进去....
介绍与用法这篇文章Introduction to Contexts in React.js说得很清楚~
问题场景
需要在Parent中声明context,在Children中拿到context并打印出来。
var Children = React.createClass({
contextTypes: {
value: React.PropTypes.string
},
render: function() {
return
<div>{this.context.value || '并没有context'}</div>
;
}
});
var Parent = React.createClass({
childContextTypes: {
value: React.PropTypes.string
},
getChildContext: function() {
return { value: '上下文' };
},
render: function() {
return (
<div>
{this.props.children}
</div>
);
}
});
var App = React.createClass({
render: function() {
return (
<Parent>
<Children />
</Parent>
);
}
});
React.render(React.createElement(App), document.body);
这样执行完后屏幕上应该是『上下文』三个打字,但事实是
以及warning
那么问题来了,上下文为何失效了呢?!为什么Children拿不到Parent里面的context呢?!
find the problem
各种google之后发现gaearon大神在issue中的回复。
原来现在0.13.3版本的react context的传递规则是owner规则,在刚才的例子中虽然Children
的parent为Parent
,但是App
才是Children
与parent共同的owner,this.context只能拿到owner传递规则的context。
尼玛,跟想象中的不一样啊!你props、render的规则不都是Parent规则么!
不继续吐槽,那么按照这个思路把context放在App上,Parent与Children应该都能成功拿到Context了吧。
代码是这样的:
var Parent = React.createClass({
contextTypes: {
value: React.PropTypes.string
},
render: function() {
return (
<div>
{this.context.value && '可算拿到了...' }
{this.props.children}
</div>
);
}
});
var App = React.createClass({
childContextTypes: {
value: React.PropTypes.string
},
getChildContext: function() {
return { value: '上下文' };
},
render: function() {
return (
<Parent>
<Children />
</Parent>
);
}
});
React.render(React.createElement(App), document.body);
结果是这样的:
看来context成功被拿到,看到这里大家应该明白React context的机制了把。
how to get parent context
虽然明白了原理,但是问题并没有解决。我就是希望Chilren拿到Parent中的context,而不是拿到App中的context啊。我目前一共找到了两种方式可以在现阶段获取parent context。
1. use the callback
通过接收回调函数而不是react.element,然后在Parent中进行render,那么render的内容的owner自然就是Parent了,从而可以成功拿到Parent中的context。
var Parent = React.createClass({
childContextTypes: {
value: React.PropTypes.string
},
getChildContext: function() {
return { value: '上下文' };
},
render: function() {
return (
<div>
{this.props.children() /* 注意这里是function,需要执行 */}
</div>
);
}
});
// parent接受回调函数,回调函数中的内容owner为parent
var App = React.createClass({
render: function() {
return (
<Parent>
{this.renderChild}
</Parent>
);
},
renderChild: function() {
return <Children />;
}
});
实测可以成功拿到context。
2.通过this._reactInternalInstance
这种方法虽然用起来很方便不过健壮性很差,等react更新之后没准又得改代码~
可以通过this._reactInternalInstance._context.value
拿到该element的parent context。this._reactInternalInstance._currentElement._context.value
就是默认的owner context。
var Children = React.createClass({
contextTypes: {
value: React.PropTypes.string
},
render: function() {
return
<ul>
<li>{'default context is: ' + this.context.value}</li>
<li>{'parent context: ' + this._reactInternalInstance._context.value}</li>
<li>{'owner context: ' + this._reactInternalInstance._currentElement._context.value}</li>
</ul>
;
}
});
var Parent = React.createClass({
childContextTypes: {
value: React.PropTypes.string
},
getChildContext: function() {
return { value: 'parent' };
},
render: function() {
return (
<div>
{this.props.children}
</div>
);
}
});
var App = React.createClass({
childContextTypes: {
value: React.PropTypes.string
},
getChildContext: function() {
return { value: 'app' };
},
render: function() {
return (
<Parent>
<Children />
</Parent>
);
},
});
React.render(React.createElement(App), document.body);
结果如下:
context es6 写法
由于同事问我es6下context怎么用,想到可能有些人也不清楚,在这里一并附上。个人不推荐使用es7语法。
import React from 'react';
class Children extends React.Component {
// 如果不需要在构造函数中使用可以不写,没有影响
constructor(props, context) {
super(props, context);
console.log(context);
}
render() {
return
<ul>
<li>{'default context is: ' + this.context.value}</li>
<li>{'parent context: ' + this._reactInternalInstance._context.value}</li>
<li>{'owner context: ' + this._reactInternalInstance._currentElement._context.value}</li>
</ul>
;
}
}
Children.contextTypes = {
value: React.PropTypes.string
};
class Parent extends React.Component {
getChildContext() {
return { value: 'parent' };
}
render() {
return (
<div>
{this.props.children}
</div>
);
}
}
Parent.childContextTypes = {
value: React.PropTypes.string
};
class App extends React.Component {
getChildContext() {
return { value: 'app' };
}
render() {
return (
<Parent>
<Children />
</Parent>
);
}
}
App.childContextTypes = {
value: React.PropTypes.string
};
React.render(React.createElement(App), document.body);
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。