React context 丢失问题

React context 丢失问题

文本是为了说清react context目前的机制是owner context 而不是咱们觉得的parent context 机制,并说明这二者的区别。但愿能对即将使用context的同窗有所帮助.html

什么是 context

context是为了解决component之间通讯的上下文机制,该api目前并未定稿因此react并无开放出来。最近有大量须要共享上下文的场景才去了解这个api,而后也成功被绕了进去....
介绍与用法这篇文章Introduction to Contexts in React.js说得很清楚~react

问题场景

须要在Parent中声明context,在Children中拿到context并打印出来。git

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

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

clipboard.png

以及warninggithub

clipboard.png

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

find the problem

各类google以后发现gaearon大神在issue中的回复。app

clipboard.png

原来如今0.13.3版本的react context的传递规则是owner规则,在刚才的例子中虽然Children的parent为Parent,可是App才是Children与parent共同的owner,this.context只能拿到owner传递规则的context。函数

尼玛,跟想象中的不同啊!你props、render的规则不都是Parent规则么!this

不继续吐槽,那么按照这个思路把context放在App上,Parent与Children应该都能成功拿到Context了吧。google

代码是这样的:

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

结果是这样的:

clipboard.png

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

结果以下:

clipboard.png

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);
相关文章
相关标签/搜索