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目前只支持函数组件

相关内容

热门资讯

今年长江干线港口货物吞吐量创新...   众多货船行驶在长江江苏南通段江面上(11月5日摄,无人机照片)。  长江航务管理局12月29日召...
《中华人民共和国网络安全法》修...   来源:“网信中国”微信公众号
中国正能量|锚定时代坐标,砥砺...   近日,以“奋斗的你我 奋进的中国”为主题的2025中国正能量网络精品征集展播活动正式启动。活动旨...
视频丨日本学者:高市错误言论无...   近日,日本体育大学宪法学教授清水雅彦表示,日本政府在台湾问题上的立场在1972年《中日联合声明》...
全球媒体聚焦 | 美媒:国际游...   美国《旅行与旅游世界》网站12月28日发表题为《国际游客涌入中国欢度新年假期》的文章。文章说,2...
外媒观察|“十四五”与“十五五...   中国网12月29日讯(记者李智)随着新年的临近,中国经济再次成为全球聚焦的议题。在“十四五”规划...
2025,他们来中国了   2025年,中国持续扩大免签国家范围,“China Travel”愈加火爆。从到中国直播吸粉的顶...
扫地机器人巨头宣布:给全员发黄... 据红星新闻12月28日报道,追觅科技创始人俞浩宣布,公司年终奖加码,在常规年终奖金之外,额外奖励全体...
企业慰问金财税处理全指南 企业慰问金财税处理的重要性在企业日常经营中,向环卫工人等外部人员发放慰问金是常见的社会责任体现。然而...
探矿权划转增值税政策解析 探矿权划转的税务处理概述随着我国矿产资源管理体制的不断完善,探矿权作为重要的无形资产,其税务处理问题...