React Hooks 基础、实现、原理
创始人
2024-05-08 17:00:30
0

React Hooks 基础、实现、原理

  • 题外话
    • 为什么要有Hooks?
        • 但是Class Component 的用法也有缺陷:
          • 1.组件复用变的困难
          • 2.JavaScript本身的缺陷
        • 函数式
    • React Hooks
        • useState
        • useEffect
        • useCallback、useMemo
        • useReducer
    • 最后

题外话

2023了,新年快乐!转眼间就已经工作一年左右了,这一年提升很多,想到很久没有整理知识点了所以…
在这里插入图片描述

为什么要有Hooks?

官方给出的解释是:复杂组件变得难以理解、组件之间复用状态逻辑很难

React是以组件为粒度编排应用的,组件是代码复用的最小单元。
在设计上,React采用props属性来接收外部的数据,使用state属性来管理组件自身产生的数据(状态),也就是说props 是传递给组件的(类似于函数的形参),而 state 是在组件内被组件自己管理的(类似于在一个函数内声明的变量)。而为了实现(运行时)对数据变更做出响应需要,React采用基于类(Class)的组件设计。除此之外,React认为组件是有生命周期的。

但是Class Component 的用法也有缺陷:

1.组件复用变的困难

HOC是React中用于重用组件逻辑的一种高级技术实现模式,它本身是一个函数,接受一个组件并返回一个新的组件。 而HOC容易产生嵌套地狱,每一次HOC调用都会产生一个组件实例。

2.JavaScript本身的缺陷

稍微不慎就会出现因this的指向报错。没有类似Java/C++多继承的概念,类的逻辑复用是个问题。

函数式

当然React不只是只有类式写法,还有函数式,但是早期的函数式组件只是单纯地接收props、绑定事件、返回jsx,本身是无状态的组件,依赖props传入的handle来响应数据(状态)的变更,所以函数式组件不能脱离Class Comnent来存在。
这时候就诞生了Hook,使得组件自身能够通过某种机制再触发状态的变更并且引起re-render

React Hooks

useState

useState可以管理组件自身定义的状态,返回一个 state,以及更新 state 的函数。
如下:setCount函数用于更新 count,它接收一个新的 state 值并将组件的一次重新渲染加入队列,并且引起组件re-render。在组件在初始渲染期间,返回的状态 (state) 与传入的第一个参数值相同也就是0。
并且count只能通过setCount来改变。

  import React, { useState } from 'react';function Add() {const [count, setCount] = useState(0);return (

You clicked {count} times

);}

dispatchAction函数是更新state的关键,它会生成一个update挂载到Hooks队列上面,并提交一个React更新调度,后续的工作和类组件一致。
理论上可以同时调用多次dispatch,但只有最后一次会生效(queue的last指针指向最后一次update的state)
useState更新数据是直接覆盖的。

// useState() 首次render时执行mountState
function mountState(initialState) {// 从当前Fiber生成一个新的hook对象,将此hook挂载到Fiber的hook链尾,并返回这个hookvar hook = mountWorkInProgressHook();hook.memoizedState = hook.baseState = initialState;var queue = hook.queue = {last: null,dispatch: null,lastRenderedReducer: (state, action) => isFn(state) ? action(state) : action,lastRenderedState: initialState};// currentlyRenderingFiber$1保存当前正在渲染的Fiber节点// 将返回的dispatch和调用hook的节点建立起了连接,同时在dispatch里边可以访问queue对象var dispatch = queue.dispatch = dispatchAction.bind(null, currentlyRenderingFiber$1, queue);return [hook.memoizedState, dispatch];
} 功能相当于setState!
function dispatchAction(fiber, queue, action) {...var update = {action, // 接受普通值,也可以是函数next: null,};var last = queue.last;if (last === null) {update.next = update;} else {last.next = update;}// 略去计算update的state过程queue.last = update;...// 触发React的更新调度,scheduleWork是schedule阶段的起点scheduleWork(fiber, expirationTime);
}

useEffect

useEffect 可以在组件渲染后实现各种不同的副作用。有些副作用可能需要清除,所以需要返回一个函数,有些不需要。

import React, { useState, useEffect } from 'react';function Example() {const [count, setCount] = useState(0);// Similar to componentDidMount and componentDidUpdate:useEffect(() => {// Update the document title using the browser APIdocument.title = `You clicked ${count} times`;});return (

You clicked {count} times

); }

在函数组件主体内(这里指在 React 渲染阶段)改变 DOM、添加订阅、设置定时器、记录日志以及执行其他包含副作用的操作都是不被允许的,因为这可能会产生莫名其妙的 bug 并破坏 UI 的一致性。
使用 useEffect 完成副作用操作。赋值给 useEffect 的函数会在组件渲染到屏幕之后执行。。
默认情况下,effect 将在每轮渲染结束后执行,但你可以选择让它 在只有某些值改变的时候才执行,也就是说可以给 useEffect 传递第二个参数,它是 effect 所依赖的值数组,当它改变时effect 才会执行(每次改变后渲染结束),如果想执行只运行一次的 effect(仅在组件挂载和卸载时执行),可以传递一个空数组([])作为第二个参数。

 useEffect(() => {document.title = `You clicked ${count} times`;},[]);

与useState传入的是具体state不同,useEffect传入的是一个callback函数,与useState最大的不同是执行时机,useEffect callback是在组件被渲染为真实DOM后执行(所以可以用于DOM操作)
useEffect调用也会在当前Fiber节点的Hooks链追加一个hook并返回,它的memoizedState存放一个effect对象,effect对象最终会被挂载到Fiber节点的updateQueue队列(当Fiber节点都渲染到页面上后,就会开始执行Fiber节点中的updateQueue中所保存的函数)

HooksDispatcherOnMountInDEV = {useEffect: function() {currentHookNameInDev = 'useEffect';...return mountEffectImpl(Update | Passive, UnmountPassive | MountPassive, create, deps);},
};function mountEffectImpl(fiberEffectTag, hookEffectTag, create, deps) {var hook = mountWorkInProgressHook();var nextDeps = deps === undefined ? null : deps;return hook.memoizedState = pushEffect(hookEffectTag, create, undefined, nextDeps);
}function pushEffect(tag, create, destroy, deps) {var effect = {tag: tag,create: create, // 存储useEffect传入的callbackdestroy: destroy, // 存储useEffect传入的callback的返回函数,用于effect清理deps: deps,next: null};.....componentUpdateQueue = createFunctionComponentUpdateQueue();componentUpdateQueue.lastEffect = effect.next = effect;....return effect;
}function renderWithHooks() {....currentlyRenderingFiber$1.updateQueue = componentUpdateQueue;....
}

useCallback、useMemo

useCallback本质上是添加了一层依赖检查。它以另一种方式解决了问题 - 我们使函数本身只在需要的时候才改变,而不是去掉对函数的依赖。
如果count保持不变,getClickNum 也会保持不变,我们的effect也不会重新运行。但是如果count修改了,getClickNum 也会随之改变,因此会重新请求数据。
返回一个 memoized 回调函数。

 const getClickNum = useCallback(() => {return `You clicked ${count} times`;}, [count]);

useMemo用于缓存一些耗时的计算结果,只有当依赖参数改变时才重新执行计算
返回一个 memoized 值。

 const getNum = useMemo(() => getcount(count), [count]);

简单理解:useCallback(fn, deps) === useMemo(() => fn, deps)

useReducer

useReducer用于管理复杂的数据结构,基本实现了redux的核心功能。当你想更新一个状态,并且这个状态更新依赖于另一个状态的值时,就可以用useReducer。
在某些场景下,useReducer 会比 useState 更适用,例如 state 逻辑较复杂且包含多个子值,或者下一个 state 依赖于之前的 state 等。并且,使用 useReducer 还能给那些会触发深更新的组件做性能优化,因为你可以向子组件传递 dispatch 而不是回调函数 。

const initialState = {count: 0};function reducer(state, action) {switch (action.type) {case 'increment':return {count: state.count + 1};case 'decrement':return {count: state.count - 1};default:throw new Error();}
}function Counter() {const [state, dispatch] = useReducer(reducer, initialState);return (<>Count: {state.count});
}

最后

1.Hooks的调用尽量只在顶层作用域进行调用。
2.不要在循环、条件或者是嵌套函数中调用Hook,否则可能会无法确保每次组件渲染时都以相同的顺序调用Hook。
3.Hooks 的串联不是一个数组,是一个链式的数据结构,从根节点 workInProgressHook 向下通过 next 进行串联。
4.React Hooks目前只支持函数组件

相关内容

热门资讯

全谷物食品如何更多走上餐桌?   你经常吃燕麦、荞麦、藜麦吗?又是否了解哪些食物是全谷物食品?  所谓全谷物食品,是以全谷物为主要...
139.38亿件!2025年“...   记者11月12日从国家邮政局获悉,国家邮政局监测数据显示,10月21日至11月11日,全国邮政快...
阿根廷总统将不出席二十国集团约...   新华社布宜诺斯艾利斯11月12日电(记者 张铎 王钟毅)阿根廷总统米莱日前表示,将不出席本月下旬...
中央网信办严打一批涉学术论文买...   近期,部分网络账号违规提供学术论文买卖、代发、代投服务,严重扰乱网络秩序,污染网络生态。我办依法...
克宫:俄罗斯仍愿就乌克兰问题继...   中新网11月13日电 据俄媒12日报道,俄罗斯总统新闻秘书佩斯科夫表示,俄罗斯仍愿就乌克兰问题继...
中国男篮世预赛集训名单公布:周... 2025年11月12日,中国男篮正式公布了备战国际篮联2027篮球世界杯预选赛的集训名单。在这份名单...
国台办:越早实现祖国统一,越有...   “两岸关系的谈判协商:从回顾中找解方”座谈会日前在台北举行,与会人士呼吁在坚持“九二共识”基础上...
二十届四中全会名词卡片:区域创...   延伸阅读  怎样完善区域创新体系:  完善区域创新体系,关键是要统筹布局建设区域科技创新中心和产...
共享发展成果 共创美好未来 乌...   11月9日,2025年世界互联网大会乌镇峰会落下帷幕。古镇千年,是历史长河里的一瞬;峰会12年,...
“十四五”以来那曲市推进高质量...   “十四五”的春风掠过羌塘草原,在那曲这片壮美的草原上催生出万千气象。湛蓝如洗的天空下,成群的牦牛...