前言相信大部分人都已经在使用Reacthooks了,但是在开发过程中,我们需要知道为什么。整理了最近在使用Reacthooks时遇到的一些问题。如果您有任何问题或更好的答案,请分享。目录为什么将Hooks引入React?Hooks解决mixin,HOC,renderprops,hooks有哪些问题ReactHooks原理Hooks中的闭包坑1.为什么要在React中引入Hooks?Hooks解决了什么问题1.组件复用逻辑上很难在hooks之前使用renderprops和高阶组件。renderprops接受一个组件作为props,而HOC是一个接受一个组件作为参数并返回另一个组件的函数。使用这些开发的组件创建了一个难以调试的“嵌套地狱”。2.复杂的组件有更多的状态逻辑。许多组件在第一次编写时非常简单。基本上,他们只做一件事。当你的业务逻辑变得复杂时,组件也会变得复杂。在大多数情况下,我们不太可能将组件拆分成更小的组件,因为可能会有很多共享状态逻辑。拆分之后,组件之间的通信会非常复杂,甚至需要参考Redux来管理组件之间的通信。状态。3、班级学习成本高。要想用好类组件,必须了解ES6中的类,了解JavaScript中this的工作原理,注意绑定事件处理器,了解this目前的发展方向。详情查看React官方文档。Hook介绍2.Mixin、HOC、renderprops、hooks我们在平时开发中经常会遇到很多页面有一些共同的逻辑。我们不能每次遇到都直接复制原代码。来改吧,改的时候需要全局搜索改(很难保证不漏掉,费时费力),所以想办法reuse,mixin,HOC,renderprops等都是实现逻辑复用的方式。mixinvue和react都用过mixin(react目前已经废弃)mixin(混入)本质上就是将一个对象复制到另一个对象。constmixin=function(obj,mixins){constnewObj=obj;newObj.prototype=Object.create(obj.prototype);for(letpropinmixins){if(mixins.hasOwnProperty(prop)){newObj.prototype[prop]=mixins[prop];}}returnnewObj;}constobj={sayHello(){console.log('hello');}};constotherObj=function(){console.log('otherObj');}constObj=mixin(otherObj,obj);consta=newObj();//otherObja.sayHello();//hellomixin有几个问题:相关依赖:mixin可能依赖其他mixin,当我们修改其中一个mixin时,可能会影响其他mixin命名冲突:不同的人在写的时候很可能会出现命名冲突,比如handleChange而相似的通用名称增加了复杂度:当我们引入太多的组件时,使用mixin时,代码逻辑会非常复杂,因为state是不断被引入的,这与我们最初认为每个组件只有单一功能的想法背道而驰。HOCHOC是React社区提出的一种替代mixin的新方式。高阶函数是函数式编程中的一个基本概念,描述的是接受一个函数作为输入或者返回一个函数的函数,比如map、reduce等都是高阶函数。高阶组件(higher-ordercomponents),类似于接受一个组件作为参数并返回另一个组件的高阶组件。functiongetComponent(WrappedComponent){returnclassextendsReact.Component{render(){return;}};}HOC的优点是:不会影响组件内部的stateHOC是的:需要在原有组件上进行包裹和嵌套。如果大量使用HOC,就会出现大量嵌套,调试起来非常困难。HOC可以劫持props,不遵守协议也可能引起冲突你好{data.target})}/>在ReactRouterRenderPropsHome
}/>路由器>被使用。它有什么问题?容易造成“嵌套地狱”使用hooks的具体实现是通过一个函数封装状态相关的逻辑,将这些逻辑从组件中抽取出来,在这个函数中,我们可以使用其他的Hooks,单独测试,甚至将它贡献给社区。import{useState,useEffect}from'react';functionuseCount(){const[count,setCount]=useState(0);useEffect(()={document.title=`你点击了${count}times`;});returncount}hooks的引入就是为了解决上面提到的问题,因为使用函数式组件,我们在开发组件的时候,可以像往常一样自由编写函数。函数重用相对容易。直接传递不同的参数就可以渲染出不同的组件。对于复杂组件的实现,我们可以完全封装几个函数,每个函数只负责一件事。而且,我们可以抽取很多常用的代码逻辑和一些场景,封装成自定义的hooks。比如UmiHooks库封装了很多共享逻辑,比如useSearch,封装了异步搜索场景的逻辑;比如useVirtualList,它封装了虚拟List逻辑。3、ReactHooks的原理在使用hooks的时候,你可能会对它的规则有很多疑问,比如:只能在顶层使用Hooks,不能在循环、条件判断、函数嵌套中调用hooks。Hooks只在React函数中调用,不能在普通函数中调用。React是怎么知道哪个state对应哪个useState的……我们先看看官方文档给出的解释。每个组件内部都有一个“记忆单元”列表。它们只不过是我们用来存储一些数据的JavaScript对象。当您使用useState()调用Hook时,它会读取当前单元格(或在第一次渲染时对其进行初始化)并将指针移动到下一个单元格。这就是为什么多个useState()调用获得自己独立的本地状态的原因。在React中,是以类似单链表的形式实现的,所有的hook通过next依次连接起来。可以看看源码部分exporttypeHook={|memoizedState:any,baseState:any,baseQueue:Update|空,队列:UpdateQueue|空,下一个:挂钩|空,|};导出类型效果={|标记:HookEffectTag,创建:()=>(()=>void)|无效,销毁:(()=>无效)|void,deps:数组|空,下一个:效果,|};有关更详细的建议,请参阅ReactHooks原则和React的钩子系统的幕后。4、Hooks中的闭包坑先来看一下使用setState的更新机制:React在setState函数的实现中,会根据一个变量isBatchingUpdates判断是直接更新this.state还是放入队列中。而isBatchingUpdates默认是false,也就是说setState会同步更新this.state。但是有一个函数batchedUpdates,它会修改isBatchingUpdates为true,当React在调用事件处理程序之前调用这个batchedUpdates时,结果是React控制的事件处理程序进程setState不会同步更新这个.state。知道了这一点,让我们看下面的两个例子。以下代码输出什么?类示例扩展React.Component{constructor(){super();this.state={val:0};}componentDidMount(){this.setState({val:this.state.val+1});控制台日志(this.state.val);//第一条日志this.setState({val:this.state.val+1});控制台日志(this.state.val);//第二次记录setTimeout(()=>{this.setState({val:this.state.val+1});console.log(this.state.val);//第三次记录1this.setState({val:this.state.val+1});console.log(this.state.val);//第4次记录2},0);}render(){返回空值;}};打印结果为:0,0,2,3。第一次和第二次都在react自身的生命周期内,触发isBatchingUpdates为true,所以不会直接执行updatestate,而是会添加dirtyComponents,所以在打印setState的时候会获取两次update之前的state,获取到的this.state.val全为0,所以在执行的时候0设置为1,在react内部会合并,只执行一次。设置完成后state.val的值为1。setTimeout中的代码,触发时,isBatchingUpdates为false,所以可以直接更新,所以如果上面的代码是连接输出2和3,那么使用reacthooksimportReact,{useEffect,useState}from'react';constMyComponent=()=>{const[val,setVal]=useState(0);useEffect(()=>{setVal(val+1);console.log(val);setVal(val+1);console.log(val);setTimeout(()=>{setVal(val+1);控制台.log(val);setVal(val+1);console.log(val);},0)},[]);返回空};导出默认的MyComponent;printout:0,0,0,0。更新方式没有改变。首先,因为useEffect函数只运行一次,其次,setTimeout是一个闭包,内部获取的值val始终是初始化时声明的值,所以访问的值始终为0。以实例为例,update不执行操作。在这种情况下,需要一个容器,您可以在其中写入更新的状态值并稍后在setTimeout中访问它,这是useRef的一个用例。可以将state值与ref的current属性同步,在setTimeout中读取当前值。这部分的细节可以查看ReactuseEffect的陷阱。参考2019年17道高频React面试题和UmiHooks详解——助力拥抱ReactHooksReactHooks原理9102,作为前端,你必须知道hooks的玩法。ReactuseEffect的陷阱【React深入】从Mixin到HOC再到Hook