【React源碼解讀】Context && ConcurrentMode

context

在線源碼地址:https://github.com/facebook/react/blob/master/packages/react/src/ReactContext.jscss

兩種實現方式react

  • childContextType (17版本將被廢棄)
  • createContext (16版本更新)

爲何要棄用舊的api?git

老的api對context的提供方下層全部組件影響太大了,它會致使它下層全部的組件(即使該組件在沒有更新的狀況下),它每次更新的狀況下,都會從新渲染github

demoapi

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>
)

源碼app

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更新的過程當中,優先執行一些較高的任務。dom

<ConcurrentMode>
    <Parent />
  </ConcurrentMode>

ConcurrentMode有一個特性,在一個子樹當中渲染了ConcurrentMode以後,它下面的全部節點產生的更新 都是一個低優先級的更新async

demoide

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的?後續慢慢深刻學習
*/
相關文章
相關標籤/搜索