美文网首页
React context 丢失问题

React context 丢失问题

作者: yeatszhang | 来源:发表于2016-06-17 22:27 被阅读975次

    文本是为了说清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);
    

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

    clipboard.pngclipboard.png

    以及warning

    clipboard.pngclipboard.png

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

    find the problem

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

    clipboard.pngclipboard.png

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

    结果是这样的:

    clipboard.pngclipboard.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.pngclipboard.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);
    

    相关文章

      网友评论

          本文标题:React context 丢失问题

          本文链接:https://www.haomeiwen.com/subject/vmbvdttx.html