# 1-React中hooks的优缺点是什么

react中,hooks是一个非常抽象的概念,对初学者,往往不是很友好,比较一下Reacthooks的优缺点

# 优点

[1]. 代码的可读性强,使用hooks之前,发布/订阅自定义事件需要挂载到componentDidMount生命周期上面,然后需要在componentWillUnmount生命周期中清除,在使用hooks之后,通过useEffect,可以把componentDidMount生命周期,componentDidUpdate生命周期,还有componentWillUnmount生命周期集中在一起,方便代码的维护

[2]. 组件的层级更浅,在使用hooks之前,通常使用高阶组件Hoc的方法来实现多个组件共用状态,增强组件的功能,这样是增加了组件渲染的开销,影响了性能,但是在Hooks中可以使用自定义hooks组件useXXx()的方法将多个组件之间共用的状态放到自定义hooks就可以轻松的做到状态的共享

[3]. 不用在考虑class类组件this的指向问题了,在hooks组件中不需要使用this.state来获取数据和方法了

[4]. 可以从组件中提取状态逻辑,使得这些逻辑可以单独测试并复用,Hook使你在无需修改组件结构的情况下复用状态逻辑,这使得在组件间共享Hook变得更便捷,也就是可以大大减少冗余的代码,尤其是针对那些需要复用逻辑的场景

[5]. 没有破坏性改动,Hook不会影响对React概念的理解,Hook为已知的React概念提供了更直接的API,props,state,context,refs以及生命周期,同时Hook提供了一种强大的方式来组合它们

[6]. 更易于测试: 由于hooks是纯JavaScript函数,因此他们易于编写单元测试并模拟

# 缺点

[1]. 一个useEffect里面不能写太多东西,把每个不同的功能分给多个useEffect来使用,分成多个模块,把每个功能块分开来写遵循了软件设计当中的单一职责模式,hooksuseEffect只包括conponentDidMount,componentDidUpdatecomponentWillUnmount这三个生命周期,对于其他的class类组件的生命周期却不支持

[2]. 不要在class组件中调用hook,这样是无效的,不能完全模拟类组件的生命周期,虽然可以使用useEffect hook来模拟,但是它使用起来需要更多的思考和规划

[3]. Hooks是一种新的特性,存在一些兼容性的问题,相对类组件方式,学习曲线比较陡峭,需要一些时间来适应这种编程模式

React中hooks的优点主要是提高了代码的可读性和性能,方便代码的维护和迭代,同时也可以更好地实现状态共享,但是也需要在使用中结合具体的场景和需求来选择最合适的方式,无非就是有了hook,多了一种技术方案选择

# 2-React事件绑定原理

React中,事件绑定采用驼峰命名方式,而不是DOM元素中的小写字母命名方式,例如:onclick要写成onClick,onchange要写成onChange

React中绑定的事件不是原生事件,而是由原生事件合成的React事件,例如:click事件合成为onClick事件,blur,change,input,keydown,keyup等合成为onChange,React这么做的原因是为了消除不同浏览器之间的差异

React事件的工作原理主要分为以下几个步骤

[1]. 收集事件监听器:React会将事件监听器收集到一个数组中,其中包括目标元素的监听器和根元素的监听器

[2]. 获取所有事件:React会将所有事件名处理成domEventNamereactEventName,即react事件名和dom事件名的对应关系

React会遍历simpleEventPluginEvents列表,将事件名处理成domEventNamereactEventName,例如:click事件对应着onClick事件

一共有75个映射关系,registationNameDependencies则保存着react事件名和依赖事件名之间的关系,例如:onClick事件依赖于click事件

[3]. 特殊处理:对于onDoubleClick,onFocusonBlur这三个事件,他们的reactEventName与对应的domEventName不通,因此需要特殊处理

[4]. 收集合成事件:React会将event对象处理成合成事件,为了消除不同浏览器之间的差异,React设计了normalize函数来将event对象处理成合成事件

如果normalize存在,说明propName对应的属性在合成事件中是一个函数,否则,propName对应的属性是一个原生事件

React v17中,React不会在将事件处理添加到document上,而是将事件处理添加到渲染React树的根DOM容器中

const rootNode = document.getElementById('root');
ReactDOM.render(<App />,rootNode); 
1
2

React16及之前版本中,React会对大多数事件进行document.addEventListener()操作,React V17开始会通过调用rootNode.addEventListener()来代替

总之,React事件绑定的原理是通过使用合成事件来将浏览器原生事件(如clickkeyup等)封装成一个跨浏览器可靠的事件池

React组件中通过使用事件处理函数来监听这些合成事件。React使用了一些优化策略来提高事件绑定的性能和效率

比如,React会在组件卸载时自动销毁事件绑定。另外,React还支持一些高级特性,如事件代理、事件委托以及捕获和冒泡等。

可以通过event对象来获取事件的相关信息,如事件类型、触发元素、按下的键等

在使用React事件绑定时,应该避免直接操作DOM元素,而应该通过调用组件的setState方法来实现状态更新从而触发重新渲染

# 3-React中setState是同步还是异步的?

React中,setState既可以是同步的也可以是异步的,这取决于执行时机和执行的上下文

[1]. setState的异步并不是指内部由异步代码实现的,实际上执行的过程和代码都是同步的,只是合成事件和钩子函数调用的顺序在更新之前,导致在合成事件和钩子函数中不能立即看到state的变化,而在原生事件和setTimeout中,setState是同步的

[2]. 在React17中,setState是批量执行的,因为执行前会设置executionContext,但如果在setTimeout,事件监听器等函数中,就不会设置executionContext的,这时候setState会同步执行,可以在外面包一层batchUpdates函数,手动设置下excutionContext来切换成异步批量执行

[3]. 在React的渲染流程中,setState会创建update对象挂到fiber对象上,然后耨调度performSyncWorkOnRoot重新渲染,一个主要任务的先后顺序是:render阶段render函数执行-->commit阶段真实DOM替换--->setState回调函数执行callback,因此,可以看出setState的执行顺序是在render之后,commit之前

[4]. 如果ExecutionContext为0,表示当前没有正在进行的其他任务,则setState是同步的,在React的源码中,当ExecutionContext为0时,setState是同步的

[5]. 批量更新:多个顺序的setState不是同步的一个一个执行的,而是会一个一个加入队列,然后最后一起执行,在合成事件和生命周期钩子中,setState更新队列时,存储的是合并状态,因此,前面设置的key值会被后面的覆盖,最终只会执行依次更新

综上所述,setState既可以是同步的也可以是异步的,具体取决于执行时机和执行的上下文,在React中,如果需要手动控制setState的异步执行

也就是在合成事件和生命周期函数中是异步的,在原生事件和定时器中都是同步的,setState本身不分同步或异步,而是取决于是否处于batch update

可以使用batchUpdates函数手动设置excutionContext来切换成异步批量执行,同时,在合成事件和生命周期钩子中,setState更新队列时,会存储合并状态,因此需要注意key值的覆盖问题

白色

关注公众号

一个走心,有温度的号,同千万同行一起交流学习

加作者微信

扫二维码 备注 【加群】

扫码易购

福利推荐