当前位置: 首页 > Web前端 > HTML

深入探讨react-hooks的实现原理

时间:2023-03-28 11:23:26 HTML

reacthooks实现Hooks解决了哪些问题在React的设计理念中,可以简单的用下面的公式来表达:UI=f(data)等于UI左边代表最终绘制的界面;等号右边是一个函数,也就是我们写的React相关的代码;data就是数据,在React中,data可以是state也可以是props。UI是将数据作为参数传递给f的结果。这个公式的意思是,如果要渲染界面,不要直接操作DOM元素,而是修改数据,让数据驱动React去修改界面。我们开发者要做的就是设计一个合理的数据模型,让我们的代码能够根据数据来描述界面应该是什么样子,而不用担心浏览器中如何操作DOM树结构。总体设计原则:界面完全由数据驱动。一切都是一个组件。使用props进行组件间通信会带来哪些问题?组件之间的数据交换耦合度太高,很多组件之间需要共享的数据需要一层一层的传递;传统的解决方案呢!变量提升的高层函数透传引入第三方数据管理库。以上redux和mobx的三种设计方式都是将数据提升到父节点或最高节点,然后逐层传递ClassComponet生命周期的学习成本,并且强关联代码逻辑由于生命周期的执行过程hook函数,代码需要强行拆分;common:classSomeComponentextendsComponent{componetDidMount(){constnode=this.refs['myRef'];node.addEventListener('mouseDown',handlerMouseDown);node.addEventListener('mouseUp',handlerMouseUp)}...componetWillunmount(){constnode=this.refs['myRef'];node.removeEventListener('mouseDown',handlerMouseDown)node.removeEventListener('mouseUp',handlerMouseUp)}}可以说,随着Hooks的出现,上述问题将迎刃而解。按照官方的说法,hooks是完全向后兼容的,类组件不会被移除。作为开发人员,您可以慢慢迁移到最新的API。Hooks主要分为三种:Statehooks:允许函数组件使用stateEffecthooks:允许函数组件使用生命周期和副作用Customhooks:根据提供的useState、useReducer、useEffect、useRef等自定义你需要的hooks通过反应。让我们去了解Hooks。我们首先接触到的是Statehooks,useState是我们第一次接触ReactHooks。它的主要功能是让FunctionComponent使用state,接受一个参数作为state的初始值,并返回当前state和dispatch。从“反应”导入{useState};functionExample(){//声明一个新的状态变量,我们称之为“count”const[count,setCount]=useState(0);return(

你点击了{count}次

setCount(count+1)}>点击我
);}whereuseState可以多次声明;functionFunctionalComponent(){const[state1,setState1]=useState(1)const[state2,setState2]=useState(2)const[state3,setState3]=useState(3)return
{state1}{...}
}对应的钩子也有useReducer。如果一个状态对应不同类型的更新处理,可以使用useReducer。第二个接触到的是EffecthooksuseEffect的使用是让FunctionComponet组件有life-cyclesdeclarationcycle函数;比如componetDidMount,componetDidUpdate,shouldComponentUpdate,componetWiillunmount都是在这个函数中执行的,调用useEffect。这个功能有点类似于Redux的subscribe,会在每次props和state触发render之后执行。(在组件第一次渲染和每次更新后触发)。为什么叫useEffect?官方解释:因为我们在生命周期中通常会做很多操作,所以会出现一些副作用(sideeffect)的操作,比如更新DOM,获取数据等。useEffect就是使用:importReact,{useState,useEffect}from'反应';functionuseMousemove(){const[client,setClient]=useState({x:0,y:0});useEffect(()=>{consthandlerMouseCallback=(e)=>{setClient({x:e.clientX,y:e.clientY})};//组件第一次渲染后,调用document.addEventListener('mousemove',handlerMouseCallback,false);return()=>{//执行document.removeEventListener('mousemove',handlerMouseCallback,false);}})returnclient;}其中useEffect仅在组件第一次渲染后调用,也就是在didMount之后,和unmount组件时调用,也就是unmount之后。如果需要在DOM更新后同步执行,可以使用useLayoutEffect。我最后接触的是自定义钩子。根据官方的useXXXAPI结合自己的业务场景,可以使用自定义开发所需的自定义hooks,从而提取业务开发数据并按需导入;实现业务数据和视图数据的完全解耦。通过上面的Hooks实现方法之后,你应该对hooks的使用有了基本的了解。下面结合hooks的源码,剥离hooks如何保存无状态组件的原理。Hooks源码是Reactreact-reconclier**中的ReactFiberHooks.js,代码600行,理解起来非常方便。Hooks的基本类型:typeHooks={memoizedState:any,//指向当前渲染节点FiberbaseState:any,//初始化initialState,每次dispatchnewStatebaseUpdate:Update|null,//当前需要更新的Update,每次更新后,都会赋值上一次的更新,这样React就处于渲染错误的边缘,数据返回到队列中:UpdateQueue|null,//UpdateQueue接下来传递:Hook|null,//链接到下一个hooks,通过next把每个hooks串联起来}typeEffect={tag:HookEffectTag,//effectTag标记当前hook作用于life的哪个阶段-cyclescreate:()=>mixed,//初始化回调destroy:(()=>mixed)|null,//卸载回调deps:Array|null,next:Effect,//同上};ReactHooks全局维护一个workInProgressHook变量。每次调用HooksAPI时,都会先调用createWorkInProgressHooks函数。functioncreateWorkInProgressHook(){if(workInProgressHook===null){//这是列表中的第一个钩子if(firstWorkInProgressHook===null){currentHook=firstCurrentHook;if(currentHook===null){//这是一个新挂载的钩子workInProgressHook=createHook();}else{//克隆当前钩子。workInProgressHook=cloneHook(currentHook);}firstWorkInProgressHook=workInProgressHook;}else{//已经有一个正在进行的工作。重复使用它。currentHook=firstCurrentHook;workInProgressHook=firstWorkInProgressHook;}}else{if(workInProgressHook.next===null){lethook;if(currentHook===null){//这是一个新挂载的钩子hook=createHook();}else{currentHook=currentHook.next;if(currentHook===null){//这是一个新挂载的钩子hook=createHook();}else{//克隆当前钩子。钩=cloneHook(currentHook);}}//附加到列表的末尾workInProgressHook=workInProgressHook.next=hook;}else{//已经有一个正在进行的工作。重用它.workInProgressHook=workInProgressHook.next;currentHook=currentHook!==null?当前钩子.下一个:空;}}returnworkInProgressHook;}参考前端高级面试题详细答案假设我们需要执行如下hooks代码:functionFunctionComponet(){const[state0,setState0]=useState(0);const[state1,setState1]=useState(1);useEffect(()=>{document.addEventListener('mousemove',handlerMouseMove,false);.........return()=>{.........document.removeEventListener('mousemove',handlerMouseMove,false);}})const[satte3,setState3]=useState(3);return[state0,state1,state3];}当我们理解了ReactHooks的简单原理后,Hooks的串联并不是一个数组,而是一个链式数据结构,从根节点workInP开始rogressHook是通过next向下序列化的,这就是Hooks不能嵌套,不能用于条件判断,不能用于循环的原因。否则链结构将被破坏。问题一:如何将useState派发函数绑定到它使用的FunctionComponent上我们先来看一段代码:importReact,{useState,useEffect}from'react';从'react-dom'导入ReactDOM;constuseWindowSize=()=>{let[size,setSize]=useState([window.innerWidth,window.innerHeight])useEffect(()=>{lethandleWindowResize=event=>{setSize([window.innerWidth,window.innerHeight])}window.addEventListener('resize',handleWindowResize)return()=>window.removeEventListener('resize',handleWindowResize)},[])returnsize}constApp=()=>{const[innerWidth,innerHeight]=useWindowSize();返回(
  • innerWidth:{innerWidth}
  • innerHeight:{innerHeight}
)}ReactDOM.render(,document.getElementById('root'))useState的作用是让FunctionComponent具备State的能力,但是对于开发者来说,只要在FunctionComponent中引入hooks函数,dispatch之后,它将能够在当前组件上准确运行。一不小心,就会有这个疑问。带着这个疑问,阅读源码//解析当前渲染的Fiberletfiber=(currentlyRenderingFiber=resolveCurrentlyRenderingFiber());workInProgressHook=createWorkInProgressHook();//这部分源码这里省略了.........//dispathAction会绑定当前渲染的Fiber,重点是dispatchActionconstdispatch=dispatchAction.bind(null,currentlyRenderingFiber,queue,)return[workInProgressHook.memoizedState,dispatch];}functiondispatchAction(fiber,queue,action){constalternate=fiber.alternate;constupdate:Update={expirationTime,action,eagerReducer:null,eagerState:null,next:null,};.........scheduleWork(fiber,expirationTime);}

最新推荐
猜你喜欢