createContext
之前也有,相当于自动向下传递的,子树中的任意组件都可以从中按需取值(配合声明)
像一样,的作用也是自上而下传递数据,通常用于多语言配置、主题和数据缓存等场景,这些场景有几个特点:
同一份数据需要被多个组件访问
这些组件处于不同的嵌套层级
从数据传递的角度看,是一级数据共享,是子树共享。如果没有特性的话,就需要从数据源组件到数据消费者组件逐层显式传递数据(),一来麻烦,二来中间组件没必要知道这份数据,逐层传递造成了中间组件与数据消费者组件的紧耦合。而特性能够相对优雅地解决这两个问题,就像是机制的补丁
P.S.实际上,要解耦中间组件与数据消费者组件的话,还有另一种方法:把填好数据的组件通过传递下去,而不直接传递数据。这样中间组件就不需要知道数据消费者组件的内部细节(如依赖的数据)了,只知道这个位置将被插入某个组件(也就是组件组合,类似于Vue的特性),这种思路有点IoC的意思,具体见Before You Use Context
API算是对特性的重新实现(可替代之前的):
P.S.旧的 API在仍然可用,但之后会被移除掉
只维护(没有),创建时给定默认值,通过组件写,通过组件来读
一个可以对应多个,内层能够重写外层的值(实际上会从组件树中与之匹配的最近那里拿到值),的 发生变化时会通知所有后代重新渲染(直接通知,不走)
P.S.默认值比较有意思,如果没有与之匹配的,就走。作用是在单测等场景,可以不需要自己跑
P.S.比较新旧,确定是否发生了变化,走的是Object.is()浅对比逻辑(引用类型只比较引用)
内部实现
类型定义如下:
看起来比较奇怪,带两份等值属性是为了支持多并发工作(使之互不影响):
As a workaround to support multiple concurrent renderers, we categorize some renderers as primary and others as secondary. We only expect there to be two concurrent renderers at most: React Native (primary) and Fabric (secondary); React DOM (primary) and React ART (secondary). Secondary renderers store their context values on separate fields.
和两个属性很有意思,存在循环引用:
用来校验和组件是否匹配:
实现如下:
在渲染阶段把组件身上的 转移到对象上:
读取时建立依赖关系:
fiber节点上带有依赖链表,的发生变化时通知所有依赖项,大致如下:
P.S.具体实现细节见react/packages/react-reconciler/src/ReactFiberNewContext.js
此外还有两种组件,与:
看起来比较特殊,其是个的函数
createRef
之前版本中,有2种形式:
字符串形式
函数形式
示例:
前者方便易用,后者更安全(时候会给掉,游离节点引发的内存风险降低不少)
此外,字符串还有很多缺陷:
要兼容Closure Compiler高级模式的话,必须把标识为字符串(具体见Types in the Closure Type System)
不允许单一实例有多个owner
动态字符串会妨碍VM优化
在异步批量渲染下存在问题,因为是同步处理的,需要始终保持一致
可以通过hook获取到兄弟,但破坏了组件的封装性
不支持静态类型化,在类似TypeScript的(强类型)语言中,每次用到都必须显式转换
由子组件调用的回调中无法把绑定到正确的上,例如中的会被挂在执行改回调的组件上,而不是当前
希望能够传递,能有多个,以及适应异步批处理场景……关于此话题的更多讨论,见Implement Better Refs API
第3种不是字符串也不是函数,而是个对象(故称之为对象ref):
也就是说:
这里给指定的属性,实际上是个对象(身上有个属性),所以用法是这样:
就实现而言,与之前的字符串相比,不过是包了一层对象而已。其类型定义如下:
P.S.其中的Flow类型定义表示禁止扩展()
是仅含一个 的对象,这样做有3个好处:
相对安全。与函数类似,时会被置为,一定程度上降低了内存风险
适用于函数式组件。因为对象不与组件实例强关联(不要求创建实例,函数也具有这个优势)
可传递,也能有多个。这一点比函数和字符串都强大,反正只是个对象,多个组件持有也没关系,比其它两个灵活
P.S.之所以说“一定程度上”,是因为非要这么干的话,肯定是不掉的(包的这一层引用隔离,可以轻易突破)
P.S.虽然有了新的对象,但并没有废弃前两个,3者目前的状态是:
对象:因可传递等特性,建议使用
函数:因其灵活性而得以保留,建议使用
字符串:不建议使用,并且在后续版本可能被移除掉
函数形式的提供了更细粒度的控制(fine-grain control),包括绑定、解绑的时机
P.S.对象很大程度上是作为字符串的替代品推出的,所以建议用对象,废弃字符串
forwardRef
大多数场景用不着,但在几个典型场景很关键:
触发深层的(如自动聚焦搜索框)
计算元素宽高尺寸(如JS布局方案)
重新定位DOM元素(如)
从组件角度分为两类:
DOM包装组件
高阶组件(High Order Component)
上面提到的3个场景都属于DOM包装组件,比如、、,特点是对DOM节点的包装/增强。从使用角度看,与等原生DOM节点地位一样,能构成视图,并且可交互。而交互的支持依赖对原生DOM节点的控制,比如无论包多少层,想要效果的话,最终还是要触发节点的对应行为,这种场景下,传递就成了刚需
These components tend to be used throughout the application in a similar manner as a regular DOM button and input, and accessing their DOM nodes may be unavoidable for managing focus, selection, or animations.
P.S.实际应用中,甚至见到过类似的奇技淫巧,这实际上就是对传递特性的强烈需求
而高阶组件一般是对组件功能的增强/扩展,因此天生就面临传递的问题,包了一层之后就不能直接访问了,但又没有太好的方式向下传递,所以一直是个问题(以不太优雅的方式维持链)
不使用 API的话,可以这样解决:
(摘自gaearon/dom_ref_forwarding_alternatives_before_16.3.md)
姑且称之为别名ref prop传递,说白了就是通过向下传递一个载体(),到达目标节点后与之关联起来(),类似于:
API提供了一种比较优雅的解决方案:
对比上面第一种替代方案,几乎一模一样,无非是把作为独立参数,从而避免用不叫的传递的尴尬
在高阶组件的场景,这样做:
因为接受一个函数,非常适合函数式组件,而对形式的组件不太友好,所以上例这样的高阶函数场景,实质上是通过来解决的
内部实现
与载体的思路几乎没什么区别,甚至其内部实现也差不多
先看API入口:
接受一个类型的函数作为参数,返回值是一种新的(即合法,用来描述视图结构的对象),相当于给这参数传入的函数添上了类型标识
P.S.更多合法见react/packages/shared/isValidElementType.js
内部根据该类型标识区分出来之后,做一些额外处理,包括挂载、更新和卸载3部分:
(摘自react/packages/react-reconciler/src/ReactFiberBeginWork.js、react/packages/react-reconciler/src/ReactFiberCommitWork.js,清晰起见,不太重要的部分都删掉了)
挂载阶段实际上并不关心对象的来源(无论层层传递过来的还是自己创建的都一样),更新也没什么特殊的,用新的和去,卸载就是置,实现其实比较简单
StrictMode
StrictMode is a tool for highlighting potential problems in an application.
用来开启子树严格检查,是个内置组件:
有几个特点:
不渲染UI,像一样
会为后代组件(即子树级)开启额外的检查和警告提示
仅在环境有效,不影响版本
主要有4个作用:
识别具有生命周期的组件
字符串警告
检测非预期的副作用
检测旧的 context API
P.S.以后还会添加更多功能
、字符串、旧 API检查的实际意义是保障API废弃决策可靠推进,尤其是涉及第三方依赖的场景,很难确认是否存在即将过时的API的使用,提供运行时检查能够有效提醒开发者去处理,例如:
而副作用检测对于Async Rendering特性是很有意义的,第一阶段涉及很多组件方法:
也就是说,这些函数将来(开启异步渲染特性之后)可能会被调用多次,所以要求不含副作用(即idempotent,调用多次和调用一次产生的效果完全一样)。但问题是,副作用很难被检测到,也做不到,所以做了这样一件事情:
By intentionally double-invoking methods like the component constructor, strict mode makes patterns like this easier to spot.
具体地,故意多调1次这些函数:
组件的构造函数
函数
传入的更新函数
生命周期函数
算是多少有点帮助吧,既然无法帮助解决问题,那就想办法帮助暴露问题
参考资料
Refs and the DOM
Strict Mode
React v16.3.0: New lifecycles and context API
联系ayqy
领取专属 10元无门槛券
私享最新 技术干货