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);
相關文章
相關標籤/搜索