-
Notifications
You must be signed in to change notification settings - Fork 24
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
React源码系列(五): 新 ContextAPI #5
Comments
|
@mqliutie 按错成关闭了.....sorry 现在大一点的react app 基本上都有用redux,这两个东西效果基本是一样的...所以不要太低估js计算能力 <Provider> // change prop
<B> // shouldComponentUpdate: return false; 不会触发当前组件render
<E /> 这个也不会触发render
<Consumer> // diff render
<D></D> // diff render
</Consumer>
</B>
</Provider>
<A> // change state
<B> // shouldComponentUpdate: return false; 所有child都不会触发diff与render
<C>
<D></D>
</C>
</B>
</A> |
_currentValue和_currentValue2是不是忘记讲了 |
_currentValue 和 _currentValue2 我也没看到。查了一下,这个主要是为了支持多个 renderer 并发,保证不同渲染器里 value 互不影响。其实他们的作用是一样的,只是分别给主渲染器和副渲染器使用。
|
|
@prprprus who? 🙋♂️ |
猩猩~!牛b!!! |
React16 更新了新的 Context API,在这之前官方一直都不被官方提倡使用。
Context API
截至 Reaact 16.6.3,共提供了四组 api:
React.createContext
、ReactContext.Provider
、Class.contextType
、ReactContext.Consumer
。下面我们把提供 context 的组件叫为 provider(提供者),把用到 context 组件叫做 consum(消费者)
React.createContext(defaultValue)
源码地址
该方法传入一个初始值/默认值,创建一个
ReactContext
。返回的
ReactContext
包含了我们后面要用到的Consumer
和Provider
。这里的
Consumer
其实指向的就是ReactContext
,而Provider.context
也指向了ReactContext
,方便后面值的传递与获取。细心的同学可能发现这里有两个 value 值,
_currentValue
和_currentValue2
,后面会讲到这块。ReactContext.Provider
Provider 顾名思义既 context 的提供者,我们可以给这个组件传一个
value
值来覆盖createContext
传入的默认值,当value
值变化时就会通知到子级的消费者。所以都是要配合
ReactContext.Consumer
或Class.contextType
使用。一般我们传的时候,不会直接传一个对象。
value={{name: 'jsonz'}}
,因为这样每次render的时候都会认为是全新的 Object。当 provider 的 value 变化时,会把当前 provider 的 value 赋值给
ReactContext._currentValue
,后面我们的 consum 可以直接从_currentValue
去获取最新的值Class.contextType
我们可以通过把
Class.ContextType
指向ReactContext
(也可以用static
属性),然后在类的生命周期函数或者 render 函数里面通过this.context
去获取 ReactContext 值。但是这种方式有个弊端,就是一个类的 contextType 属性只能指向一个 ReactContext。如果想要同时有多个消费者,就要用到下一小节的 React.Consumer
React 在执行
updateClassInstance
的时候,会判断该的class
有没有contextType
这个属性,如果contextType
不为空,则返回ReactContext._currentValue
,这样我们组件就能拿到最新的 contextValue 了。当然里面还有很多细节,比如调用生命周期函数
ComponentWillReceiveProps
之前会加多一个oldContext !== nextContext
的判断等等。有兴趣的可以根据我之前的系列,自己看源码 乐趣更多~
ReactContext.Consumer
ReactContext.Consumer
其实是以组件的形式 consum(消费)ReactContext 的另一种方式。比起
Class.contextType
最大的不同就是可以同时消费多个 ReactContext,而且他的子级只允许是一个 Function!ReactContext.Consumer
在收到需要更新的时候,会去拿组件自身的 currentValue 作为最新的 contextValue,再拿props.children
当 render 方法,所以我们前面说该组件的子级只能是一个 Function。此时就算子级返回的是另一个
ReactContext.Consumer
,那也只是按照刚才的逻辑再走一遍。综合使用的demo github仓库
新Context API
新的Context API其实依赖
React.CreateContext
生成的组件来维护最新的 currentValue,所以不存在被shouldComponentUpdate
阻断子级 context 更新的问题。大概的原理是
当执行
workLoop
中对fiberTree进行更新时,如果发现ReactContext.Provider
组件的值发生更新(变更)的时候,都会去广播。然后找到子级中对应的消费者consum
,把他和父级的渲染优先级改为最高优先级(第二步会用到)。当执行到某个
classComponent
时,如果这个组件是不需要更新的 (新旧 props、state一致或者shouldComponentUpdate返回了false) ,这时候会去看他子级的childExpirationTime
优先级是否足够高,如果足够高就无视当前的 shouldUpdate,把子级返回到 workLoop里面进行下一次的更新。这张流程图只是拎了一部分关于context更新的来讲,要了解React整个运行的机制可以看之前的几篇
结语
Context API的更新,最直观的进步就是通过组件解决了旧Context被中间组件
shouldComponentUpdate
阻断的问题,在一定程度上可以代替小部分的Redux使用场景。目前个人的一个小项目就没有引用redux,而是直接在 contentComponet 统一用 ContextAPI 去管理。至于性能问题 emmm 不知道用
chrome react devtool Profiler
为什么好像没测出有多大的区别...The text was updated successfully, but these errors were encountered: