转载

React context 丢失问题

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); 

这样执行完后屏幕上应该是『上下文』三个打字,但事实是

React context 丢失问题

以及 warning

React context 丢失问题

那么问题来了,上下文为何失效了呢?!为什么Children拿不到Parent里面的context呢?!

find the problem

各种google之后发现gaearon大神在 issue 中的回复。

React context 丢失问题

原来现在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); 

结果是这样的:

React context 丢失问题

看来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); 

结果如下:

React context 丢失问题

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); 
正文到此结束
Loading...