zoukankan      html  css  js  c++  java
  • 【React源码解读】Context && ConcurrentMode

    context

    在线源码地址:https://github.com/facebook/react/blob/master/packages/react/src/ReactContext.js

    两种实现方式

    • childContextType (17版本将被废弃)
    • createContext (16版本更新)

    为什么要弃用旧的api?

    老的api对context的提供方下层所有组件影响太大了,它会导致它下层所有的组件(即便该组件在没有更新的情况下),它每次更新的情况下,都会重新渲染

    demo

    import React from 'react'
    import PropTypes from 'prop-types'
    
    const { Provider, Consumer } = React.createContext('default')
    
    class Parent extends React.Component {
      state = {
        childContext: '123',
        newContext: '456',
      }
    
      getChildContext() {
        return { value: this.state.childContext, a: 'aaaaa' }
      }
    
      render() {
        return (
          <>
            <div>
              <label>childContext:</label>
              <input
                type="text"
                value={this.state.childContext}
                onChange={e => this.setState({ childContext: e.target.value })}
              />
            </div>
            <div>
              <label>newContext:</label>
              <input
                type="text"
                value={this.state.newContext}
                onChange={e => this.setState({ newContext: e.target.value })}
              />
            </div>
            <Provider value={this.state.newContext}>{this.props.children}</Provider>
          </>
        )
      }
    }
    
    class Parent2 extends React.Component {
      // { value: this.state.childContext, a: 'bbbbb' }
      getChildContext() {
        return { a: 'bbbbb' }
      }
    
      render() {
        return this.props.children
      }
    }
    
    function Child1(props, context) {
      console.log(context)
      return <Consumer>{value => <p>newContext: {value}</p>}</Consumer>
    }
    
    Child1.contextTypes = {
      value: PropTypes.string,
    }
    
    class Child2 extends React.Component {
      render() {
        return (
          <p>
            childContext: {this.context.value} {this.context.a}
          </p>
        )
      }
    }
    
    // Child2.contextType = Consumer
    
    Child2.contextTypes = { // 通过这种方式,告诉react在渲染过程中,Child2组件希望去获取它的父层组件中所有传递的context中的某几个
      value: PropTypes.string,
      a: PropTypes.string,
    }
    
    Parent.childContextTypes = { // 声明传递给子组件的context
      value: PropTypes.string,
      a: PropTypes.string,
    }
    
    Parent2.childContextTypes = { 
      a: PropTypes.string,
    }
    
    export default () => (
      <Parent>
        <Parent2>
          <Child1 />
          <Child2 />
        </Parent2>
      </Parent>
    )
    
    

    源码

    export function createContext<T>(
      defaultValue: T, 
      calculateChangedBits: ?(a: T, b: T) => number,
    ) {
        /**
        calculateChangedBits: 一个方法,用来计算新老context变化
        
        */
    
        const context: ReactContext<T> = {
            $$typeof: REACT_CONTEXT_TYPE,
            _calculateChangedBits: calculateChangedBits,
            
            _currentValue: defaultValue,
            _currentValue2: defaultValue,
           
            Provider: (null: any),
            Consumer: (null: any),
         };
         
         /**
         $$typeof: 与ReactElement的$$typeof不一样
         _currentValue和_currentValue2用处是一样的,只是用的地方不一样,比如不同的平台不一样
         _currentValue: 用来记录Prvoider上面提供的value有变化的情况下,就会更新到这个_currentValue上面,就是用来记录最新的context的值的
         
         */
    }
    
    
    

    ConcurrentMode

    16版本以后提出的功能,其目标让react整体渲染过程有一个优先级排比,并整体的渲染过程能够中断,他就可以进行一个任务的调度,更好的利用cpu性能。react能够让我们去区分一些优先级高低的任务,在进行一个react更新的过程中,优先执行一些较高的任务。

      <ConcurrentMode>
        <Parent />
      </ConcurrentMode>
    

    ConcurrentMode有一个特性,在一个子树当中渲染了ConcurrentMode之后,它下面的所有节点产生的更新 都是一个低优先级的更新

    demo

    import React, { ConcurrentMode } from 'react'
    import { flushSync } from 'react-dom' // 能够强制某一个更新操作的时候,使用一个优先级最高的更新
    
    import './index.css'
    
    class Parent extends React.Component {
      state = {
        async: true,
        num: 1,
        length: 2000,
      }
    
      componentDidMount() {
        this.interval = setInterval(() => {
          this.updateNum()
        }, 200)
      }
    
      componentWillUnmount() {
        // 别忘了清除interval
        if (this.interval) {
          clearInterval(this.interval)
        }
      }
    
      updateNum() {
        const newNum = this.state.num === 3 ? 0 : this.state.num + 1
        if (this.state.async) {
          this.setState({
            num: newNum,
          })
        } else {
          flushSync(() => {
            this.setState({
              num: newNum,
            })
          })
        }
      }
    
      render() {
        const children = []
    
        const { length, num, async } = this.state
    
        for (let i = 0; i < length; i++) {
          children.push(
            <div className="item" key={i}>
              {num}
            </div>
          )
        }
    
        return (
          <div className="main">
            async:{' '}
            <input
              type="checkbox"
              checked={async}
              onChange={() => flushSync(() => this.setState({ async: !async }))}
            />
            <div className="wrapper">{children}</div>
          </div>
        )
      }
    }
    
    
    export default () => (
      <ConcurrentMode>
        <Parent />
      </ConcurrentMode>
    )
    
    

    源码

    // React.js
    import {
      REACT_CONCURRENT_MODE_TYPE,
      REACT_FRAGMENT_TYPE,
      REACT_PROFILER_TYPE,
      REACT_STRICT_MODE_TYPE,
      REACT_SUSPENSE_TYPE,
    } from 'shared/ReactSymbols';
    ...
    
    if (enableStableConcurrentModeAPIs) {
      React.ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
      React.Profiler = REACT_PROFILER_TYPE;
    } else {
      React.unstable_ConcurrentMode = REACT_CONCURRENT_MODE_TYPE;
      React.unstable_Profiler = REACT_PROFILER_TYPE;
    }
    
    
    ...
    
    /** 
    发现ConcurrentMode竟然是一个常量,于是我们去shared/ReactSymbols下一睹
    */
    
    // ReactSymbols.js
    const hasSymbol = typeof Symbol === 'function' && Symbol.for;
    
    ...
    
    export const REACT_CONCURRENT_MODE_TYPE = hasSymbol
      ? Symbol.for('react.concurrent_mode')
      : 0xeacf;
      
    ...
    /**
    我们发现,ConcurrentMode组件就是一个Symbol,它也没有任何的属性
    
    留有疑问,它到底是如何承载chilren的?后续慢慢深入学习
    */
    
    
  • 相关阅读:
    Qt MFC 混合编程的问题
    DECLARE_MESSAGE_MAP用法
    DECLARE_DYNCREATE与DECLARE_DYNAMIC区别
    Qt unsigned char* (uchar*) 转为QImage
    C++ SafeArrayAccessData,SafeArrayUnaccessData使用
    C++ 实现 COM → IUnknown → 接口
    C++ COM编程之IUnknown接口
    C++ COM三大接口:IUnknown、IClassFactory、IDispatch。
    C++ COM组件QueryInterface函数
    C++ COM组件的AddRef和Release()方法使用
  • 原文地址:https://www.cnblogs.com/fe-linjin/p/11722880.html
Copyright © 2011-2022 走看看