重新学习react入门转行后,从vue转react已经一年半了。我没有写太多代码,所以我认为我对React有点熟悉。但是看了《beta.reactjs.org》之后,还是被震撼到了。我之前对react的理解一直是错误的。今天,小编就来谈谈理解react组件的心路历程,以及清理自己固有的认知,并尝试建立一个不容易但简单的心智模型以备日后使用:构建react组件的基本心智模型和建立useEffect的基本心智模型自我反省声明:这篇文章是我个人官方文档《beta.reactjs.org》看完后的。不保证源代码分析级别的严谨性。以后还会继续调整。建议大家有时间自己阅读文档。“原文档有点啰嗦,但整体流畅易读。”构建React组件的基本心智模型是众所周知的。React是围绕vdom和domdiff设计的。更新fiber架构后,没有变化。小编一直对react组件的理解有很多疑惑(这里为了简单起见,已经不值得一提的类组件略过,指的是函数式组件):乍一看,UI=f(state)是简洁美观几乎“零”的API公式,优雅,优雅!看来以后总能把代码写得这么优雅?没有immer之类的库的帮助,你必须手动扩展Object、Array等常见的引用类型,才能达到将不可变数据喂给React组件的效果。再见。我读过Dan的经典博文“overreacted.io/zh-hans/algebraic-effects-for-the-rest-of-us”。不是纯函数吗?很简单。React组件采用“多次不断执行组件的关联函数”的方式,以“同步代码”的形式实现“异步中断与恢复”的效果(称为代数效应)。它具有类似于异步函数的功能,但没有异步函数的传染性缺点。它不是异步,但比异步更好!回头看文档才知道react组件不仅仅是一个代数效应那么简单,而是fiber树的位置和fiber树的diff结果紧密结合的产物。即react组件通过“多次执行”实现了“零”的api抽象,每次执行都传递给它不优雅的内部系统。小编目前认为这个纯函数有各种约束,其实就是一个内置的DSL。实现“纯”功能抽象,因此必须手动设置为不可变。实现了对其他特殊能力、代数效应、状态管理、异步任务调度等的更多抽象,实现了基于diff规则的自动状态管理,仔细考虑状态是否还能保持。PS:小编开始对用react来实现“万物皆组件”的想法有点疑惑了,因为react组件已经是深度定制系统的内置DSL了,需要在以一个基本单元的形式来支持其他维度抽象的系统有点重,从react-router的六大版本的变化就可以看出来。搭建useEffect的基本心智模型小编对hooks的理解也经历了几个阶段:一开始被它的优雅和简洁所折服,以至于对于“不能在if中使用”之类的笨拙规则感到非常理解.这个时候小编只觉得是一种比较聚合的代码组织方式。后来有人说钩子本质上是响应式的。我很长一段时间都相信它们,所以我研究了很多响应式库。在度、性能、包大小上都没有优势,但react已经无力回天。最后小编总结,hooks和reactive没有直接关系,不应该强行关联。React组件通过反复执行获得超能力和特殊能力。因此,结合钩子保存状态的特性,整个函数体中声明的所有自由变量自然而然地具有了响应式的特性。不管是useState包裹的变量,还是props,甚至是普通的constb=a+1,自然就变成了reactive,可以理解为a+1的计算映射函数b的声明,所以它不仅仅是反应性的钩子。最后,既然不是为响应式目的而设计的,为什么useEffect取这样的名字呢?为什么react要在dev阶段“恶意”触发两次useEffect?小编从新文档中得到了答案。useEffect原名应该是useImmutableReactiveAfterEveryRenderDangerEffect,解释为“用于react组件本身的不可变的、响应式的、危险的、渲染后的副作用”。小编为了配合官方选择这么超长的名字,不鼓励多用,防止大家滥用这个hook。上面已经解释了Immutable和Reactive。AfterEveryRenderDangerEffect是指组件提交渲染后可以自定义的行为。例如,您希望组件在呈现后发送请求。报告一个日志。注册一个系统事件。简而言之,只有当需要一种“自动”机制来“自动”处理渲染后此刻“同步”当前反应组件和组件外部资源的危险副作用时,才必须使用它。大多数时候,你需要的主动处理逻辑应该放在事件回调中。现在,你觉得你滥用了吗?由于每个useEffect的功能、依赖更新频率和设计目的不同,所以需要将一个大的useEffect拆分成多个功能单一的小useEffects,以防止更多的混乱。而“恶意”触发useEffect两次,就是为了凸显忘记设置清理返回函数的危险。还有一件事有一个特别深的陷阱不得不提一下。你是不是经常遇到,因为要实现某些功能,你不得不在设计之初加入依赖之外的依赖,否则lint规则是通不过的。“Lint只会自动忽略组件外部的上下文变量、不可变函数等。”手动忽略lint显然是一种失控的不负责任的做法。那么,有哪些不违背设计意图的可控方案呢?将状态移出组件,或移入useImmutableReactiveAfterEveryRenderDangerEffect。使用setState(v=>...)传入函数以移除依赖数组中的反应变量。提取一个非反应性的effectFn函数“最新的useEventapi来自这里”,并使用这个函数重新捕获上下文中的反应性变量,同时也移除依赖数组中的反应性变量。【文档警告】非反应式effectFn函数是useImmutableReactiveAfterEveryRenderDangerEffect的独特作用域泄漏补丁解决方案,请勿滥用。只能被对应的useImmutableReactiveAfterEveryRenderDangerEffect内部调用,不允许传递给其他hook!自我反省本来想写re-render的心智模型的,但是发现re-render的理论在业界其实讲的挺透彻的,没什么新意可说,但是还不够好”分享”。小编发现,用喷子的角色来吐槽FE的生态漏洞,收获的只是一种失落感,因为虽然知道其中的痛点和不足,但也只能说说而已,无法正确描述、正视和解决问题。阅读这次新文件,让人有一种拨开迷雾的满足感。感谢Dan老师用心的写文档,让小编重新认识到了我的无知。最后,欢迎关注一波:来源:公众号「textcraft」或多搜「桃猪」。
