高铁上的动车 發表於 2020-2-19 15:26:00

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>&lt;Provider value={{ theme: "pink" }}&gt;
      &lt;Content /&gt;
      &lt;/Provider&gt;
<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>&lt;div&gt;
      &lt;Button /&gt;
    &lt;/div&gt;
<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>&lt;Consumer&gt;<span style="color: rgba(0, 0, 0, 1)">
      {({ theme }) </span>=&gt;<span style="color: rgba(0, 0, 0, 1)"> (
      </span>&lt;<span style="color: rgba(0, 0, 0, 1)">button
          style</span>={{ backgroundColor: theme }}&gt;<span style="color: rgba(0, 0, 0, 1)">
          Toggle Theme
      </span>&lt;/button&gt;
<span style="color: rgba(0, 0, 0, 1)">      )}
    </span>&lt;/Consumer&gt;
<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>&lt;Provider&gt;</code>的<code>value</code>时,<code>createContext</code>中的<code>defaultValue</code>不会生效,<code>Consumer</code>的<code>value</code>显示空值&nbsp;</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 &lt;Context.Consumer.Provider&gt; is not supported and will be removed in ' + 'a future major release. Did you mean to render &lt;Context.Provider&gt; 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 &lt;Context.Consumer.Consumer&gt; is not supported and will be removed in ' + 'a future major release. Did you mean to render &lt;Context.Consumer&gt; 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值&nbsp;则直接使用context.Provider._context._currentValue进行设置&nbsp;</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&nbsp;context对象</p>
<p>返回的context对象中包含Provider/Consumer/_currentValue/_calculateChangedBits计算新老context变化等值&nbsp;</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]
查看完整版本: React的React.createContext()源码解析(四)