React的React.createContext()源码解析(四)
<p><span style="font-size: 18px"><strong>一.产生context原因</strong></span></p><div>从父组件直接传值到孙子组件,而不必一层一层的通过props进行传值,相比较以前的那种传值更加的方便、简介。</div>
<p><span style="font-size: 18px"><strong>二.context的实现方式</strong></span></p>
<p>新版本(React16.x后)</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">创建两个组件 Provider,Consumer</span><span style="color: rgba(0, 128, 0, 1)">
//</span><span style="color: rgba(0, 128, 0, 1)">let {Provider,Consumer}=React.createContext(defaultValue); //defaultValue可以设置共享的默认数据 当Provider不存在的时候 defaultValue生效</span>
const { Provider, Consumer } = React.createContext({ theme: "green" })
<span style="color: rgba(0, 0, 0, 1)">class MessageList extends React.Component {
render() {
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Procider组件遍历子组件,并且有一个属性value用来提供数据</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><Provider value={{ theme: "pink" }}>
<Content />
</Provider>
<span style="color: rgba(0, 0, 0, 1)"> )
}
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">中间组件</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Content() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><div>
<Button />
</div>
<span style="color: rgba(0, 0, 0, 1)">)
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">接收组件,如果子组件是Consumer的话,将value作为参数值,传递给新创建的Consumer渲染一个函数组件</span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> Button() {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><Consumer><span style="color: rgba(0, 0, 0, 1)">
{({ theme }) </span>=><span style="color: rgba(0, 0, 0, 1)"> (
</span><<span style="color: rgba(0, 0, 0, 1)">button
style</span>={{ backgroundColor: theme }}><span style="color: rgba(0, 0, 0, 1)">
Toggle Theme
</span></button>
<span style="color: rgba(0, 0, 0, 1)"> )}
</span></Consumer>
<span style="color: rgba(0, 0, 0, 1)">)
}</span></pre>
</div>
<p><span style="font-size: 13px"><span style="color: rgba(255, 0, 0, 1)">注意</span>:将<code>undefined</code>传递给<code><Provider></code>的<code>value</code>时,<code>createContext</code>中的<code>defaultValue</code>不会生效,<code>Consumer</code>的<code>value</code>显示空值 </span></p>
<p><strong><span style="font-size: 16px">三.React.createContext()源码解析</span></strong></p>
<div class="cnblogs_code">
<pre> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">calculateChangedBits方法,使用Object.is()计算新老context变化<br> //defaultValue 当Provider组件属性value不存在时 会使用默认值defaultValue<br></span>
<span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> createContext(defaultValue, calculateChangedBits) { </span><span style="color: rgba(0, 0, 255, 1)">if</span> (calculateChangedBits ===<span style="color: rgba(0, 0, 0, 1)"> undefined) {
calculateChangedBits </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
} </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
{
</span>!(calculateChangedBits === <span style="color: rgba(0, 0, 255, 1)">null</span> || <span style="color: rgba(0, 0, 255, 1)">typeof</span> calculateChangedBits === 'function') ? warningWithoutStack$1(<span style="color: rgba(0, 0, 255, 1)">false</span>, 'createContext: Expected the optional second argument to be a ' + 'function. Instead received: %s', calculateChangedBits) : <span style="color: rgba(0, 0, 255, 1)">void</span> 0<span style="color: rgba(0, 0, 0, 1)">;
}
}
</span><span style="color: rgba(0, 0, 255, 1)">var</span> context =<span style="color: rgba(0, 0, 0, 1)"> {</span>
$$<span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">: REACT_CONTEXT_TYPE, <span style="color: rgba(0, 128, 0, 1)">//context的$$typeof在createElement中的type中的type对象中存储</span></span>
<span style="color: rgba(0, 0, 0, 1)"> _calculateChangedBits: calculateChangedBits,<span style="color: rgba(0, 128, 0, 1)">//计算新老context变化</span></span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">_currentValue和_currentValue2作用一样,只是作用平台不同</span>
_currentValue: defaultValue, <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">Provider的value属性</span>
<span style="color: rgba(0, 0, 0, 1)"> _currentValue2: defaultValue,</span>
_threadCount: 0<span style="color: rgba(0, 0, 0, 1)">, <span style="color: rgba(0, 128, 0, 1)">//用来追踪context的并发渲染器数量
</span></span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> These are circular</span>
Provider: <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">,<span style="color: rgba(0, 128, 0, 1)"> //提供组件</span>
Consumer: </span><span style="color: rgba(0, 0, 255, 1)">null<span style="color: rgba(0, 128, 0, 1)">//应用组件</span></span><span style="color: rgba(0, 0, 0, 1)">
};
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">context.Provider的_context指向context对象</span>
context.Provider =<span style="color: rgba(0, 0, 0, 1)"> {
$$</span><span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">: REACT_PROVIDER_TYPE,
_context: context
};
</span><span style="color: rgba(0, 0, 255, 1)">var</span> hasWarnedAboutUsingNestedContextConsumers = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
</span><span style="color: rgba(0, 0, 255, 1)">var</span> hasWarnedAboutUsingConsumerProvider = <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">;
{
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> A separate object, but proxies back to the original context object for</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> backwards compatibility. It has a different $$typeof, so we can properly</span>
<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> warn for the incorrect usage of Context as a Consumer.</span>
<span style="color: rgba(0, 0, 255, 1)">var</span> Consumer =<span style="color: rgba(0, 0, 0, 1)"> {
$$</span><span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">: REACT_CONTEXT_TYPE,
_context: context,
_calculateChangedBits: context._calculateChangedBits<span style="color: rgba(0, 128, 0, 1)"> //计算新老context变化</span>
}; </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> $FlowFixMe: Flow complains about not setting a value, which is intentional here</span>
<span style="color: rgba(0, 0, 0, 1)">
Object.defineProperties(Consumer, {
Provider: {
get: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">hasWarnedAboutUsingConsumerProvider) {
hasWarnedAboutUsingConsumerProvider </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
warning$</span>1(<span style="color: rgba(0, 0, 255, 1)">false</span>, 'Rendering <Context.Consumer.Provider> is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Provider> instead?'<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> context.Provider;
},
set: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (_Provider) {
context.Provider </span>=<span style="color: rgba(0, 0, 0, 1)"> _Provider;
}
},
_currentValue: {
get: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> context._currentValue;
},
set: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (_currentValue) {
context._currentValue </span>=<span style="color: rgba(0, 0, 0, 1)"> _currentValue;
}
},
_currentValue2: {
get: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> context._currentValue2;
},
set: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (_currentValue2) {
context._currentValue2 </span>=<span style="color: rgba(0, 0, 0, 1)"> _currentValue2;
}
},
_threadCount: {
get: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> context._threadCount;
},
set: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> (_threadCount) {
context._threadCount </span>=<span style="color: rgba(0, 0, 0, 1)"> _threadCount;
}
},
Consumer: {
get: </span><span style="color: rgba(0, 0, 255, 1)">function</span><span style="color: rgba(0, 0, 0, 1)"> () {
</span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">hasWarnedAboutUsingNestedContextConsumers) {
hasWarnedAboutUsingNestedContextConsumers </span>= <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">;
warning$</span>1(<span style="color: rgba(0, 0, 255, 1)">false</span>, 'Rendering <Context.Consumer.Consumer> is not supported and will be removed in ' + 'a future major release. Did you mean to render <Context.Consumer> instead?'<span style="color: rgba(0, 0, 0, 1)">);
}
</span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> context.Consumer;
}
}
}); </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty</span>
context.Consumer =<span style="color: rgba(0, 0, 0, 1)"> Consumer;
}
{
context._currentRenderer </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
context._currentRenderer2 </span>= <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">;
}
</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">返回一个context对象</span>
<span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> context;
}</span></pre>
</div>
<p>解析:</p>
<p>1._currentValue 用来记录Provider/Consumer的value值 默认为defaultValue</p>
<p>2.在context设置Provider和Consumer属性值</p>
<ul>
<li>Provider属性</li>
</ul>
<div class="cnblogs_code">
<pre> context.Provider =<span style="color: rgba(0, 0, 0, 1)"> {
$$</span><span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">: REACT_PROVIDER_TYPE,
_context: context
};</span></pre>
</div>
<p>表示给context的provider设置属性值 如果我们需要设置当前Provider上的value值 则直接使用context.Provider._context._currentValue进行设置 </p>
<ul>
<li>Consumer属性</li>
</ul>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)"> var</span> Consumer =<span style="color: rgba(0, 0, 0, 1)"> {
$$</span><span style="color: rgba(0, 0, 255, 1)">typeof</span><span style="color: rgba(0, 0, 0, 1)">: REACT_CONTEXT_TYPE,
_context: context,
_calculateChangedBits: context._calculateChangedBits
};
context.Consumer </span>= Consumer;</pre>
</div>
<p>表示给context设置Consumer设置属性值 如果我们需要获取当前通过Provider上的value值 则直接通过自身的Consumer组件的context.Consumer._context._currentValue获取value值</p>
<p>3.return context对象</p>
<p>返回的context对象中包含Provider/Consumer/_currentValue/_calculateChangedBits计算新老context变化等值 </p>
<div class="cnblogs_code">
<pre>const { Provider, Consumer } = React.createContext({ theme: "green" })</pre>
</div>
<p>通过React.createContext调用createContext方法 获取context中的Provider和Consumer组件对象 当调用React.createElement(type,config,..)作为type值进行传递</p>
<p>返回的context内容图解:</p>
<p><img src="https://img2018.cnblogs.com/common/1853661/202002/1853661-20200219172706962-472088602.png"></p><br><br>
来源:https://www.cnblogs.com/sunxiaopei/p/12319203.html
頁:
[1]