当前位置: 首页 > java >正文

React hooks详解

React hooks详解

      • 一、为什么react需要引入hooks?
      • 二、useState状态维护函数
      • 三、useEffect副作用优化
      • 四、useMemo与useCallback缓存
      • 五、useRef处理ref对象
      • 六、useContext共享状态
      • 七、useReducer优化组件
      • 八、自定义组件

一、为什么react需要引入hooks?

    react中hooks占有特别重要的地位。你可能会想,在react中有了类式组件,那么代码复用已经特别方便了,加上继承,配合typescript进行类型限制,那么整个代码就有点像Java风格了。为什么还是有特别多的人选择使用函数式组件呢,甚至还提出了专门处理函数式组件的hooks内置函数?
    React 引入 Hooks 并不是因为类组件 “不好”,而是为了提供一种更简洁、灵活的方式来处理组件的状态和副作用等功能,以满足不同的开发需求。类组件有其自身的优势,但也存在一些局限性,而 Hooks 则有效地解决了这些问题。
    对于类组件,类组件提供了诸如 componentDidMount等生命周期方法,在处理组件的初始化、更新和销毁时,能够清晰地分离和管理不同阶段的逻辑。可以通过继承来复用代码逻辑,减少重复代码。对于一些复杂的状态管理场景,类组件的 this.state 和 this.setState 可以方便地管理和更新状态,尤其是当状态之间存在依赖关系时,通过 setState 的回调函数可以确保状态更新的一致性。
    但是,类组件的语法相对繁琐,需要继承 React.Component,并且在方法中使用 this 来访问状态和方法,容易出现 this 指向问题,增加了代码的理解和维护成本。例如,为了在事件处理函数中正确使用 this,需要进行绑定操作(如 this.handleClick = this.handleClick.bind(this);)。类组件之间的逻辑复用通常通过高阶组件(HOC)或 Render Props 模式(具体来说指的是允许组件通过 props 接收一个函数并调用它来动态决定渲染内容)实现,但这些模式会增加组件的层级嵌套,导致组件树变得复杂,难以理解和调试,同时还可能引发命名冲突和性能问题。
    相比之下,Hooks 允许使用纯函数来编写组件,避免了类组件中的繁琐语法和 this 指向问题,使代码更加简洁、易读和易于维护。例如,使用 useState 钩子可以简单地在函数组件中添加状态,无需像类组件那样定义 this.state 和 this.setState。此外,Hooks 可以将相关的逻辑提取到自定义的 Hook 函数中,实现逻辑的复用,避免了高阶组件和 Render Props 模式带来的问题。例如,可以创建一个自定义的 useFetch Hook 来处理网络请求逻辑,多个组件可以复用这个 Hook,提高了代码的复用性和可维护性。通过 Hooks,可以将组件的逻辑按照功能拆分成多个小的函数,每个函数专注于一个特定的任务,使代码的结构更加清晰,便于理解和调试。最后,许多 React 库和工具(如 Redux、React Router 等)都开始提供基于 Hooks 的 API,使得在项目中使用这些库更加方便和直观,进一步提升了开发效率。

二、useState状态维护函数

    由于函数式组件本身相当于一个render函数,如果里面存在视图相关的值发生了变更,那么函数会重新执行,如果值频繁修改,那么这个函数会频繁执行,从而影响性能。于是为了维护函数式组件的状态值,Hooks中提供了专门的函数来维护——useState,首先给出useState的用法:

function App() {const [count, setCount] = useState(0);return <><button className='btn' onClick={() => setCount(count+1)}>{count}</button></>
}

    这里useState中的参数为左侧count的默认值,setCount作为唯一函数来修改count。但是这种方法依旧存在重复渲染的可能,举例如下:
在这里插入图片描述
    对这种情形进行解释,这里描述的是在函数式组件render内部修改了状态值,由于setState在异步代码中是同步执行的,因此setTimeout函数触发之后render会重新渲染,这时会重新执行一个setTimeout函数,最终导致组件会无限循环渲染。

三、useEffect副作用优化

    为了解决上面这种情况,出现了useEffect函数(可以使用多次),useEffect使用如下:
在这里插入图片描述
    useEffect接受两个参数,第一个是存在副作用的代码,第二个是依赖项,如果依赖项变化,那么会重复执行,如果是个空数组,那么只执行一次。
在这里插入图片描述
    下面以天气为例,展示怎么借助于useState和useEffect高效实现天气的动态切换,程序代码如下:

function App() {const [city, setCity] = useState("北京");const [weatherInfo, setWeatherInfo] = useState<WeatherItem[]>([]);useEffect(() => {axios.get(`http://m7200.tianqiapis.com/?version=day&unit=m&language=zh&query=${city}&appid=test66&appsecret=test666`).then(response => {const { month } = response.data;if (month && Array.isArray(month)) {setWeatherInfo(month as WeatherItem[]);}}).catch(error => {console.log("输出请求错误信息:", error);});}, [city]);return (<><input placeholder='请输入城市名称' value={city} onChange={(event) => { setCity(event.target.value); }} /><div><div>结果</div><div><ul>{weatherInfo.map((item: WeatherItem, key: number) => (<li className='weather-item' key={key}>{item.dateOfWeek}{item.night.narrative}</li>))}</ul></div></div></>);
}

在这里插入图片描述

四、useMemo与useCallback缓存

    useMemo 是 React 中的一个性能优化 Hook,用于缓存计算结果,避免在每次渲染时都进行高开销的计算。它返回一个记忆化的值,只有当依赖项发生变化时才会重新计算。例如,在React中如果父组件改变,那么父组件下的子组件也会跟着渲染,但这个过程中可能子组件根本没有变化,这个时候便可以利用useMemo来优化渲染过程。

function Box() {const [count, setCount] = useState(Math.random());return (<div><p>随机数:{Math.random()}</p><button onClick={() => setCount(count + 1)}>Box子组件的按钮,count={count}</button></div>)
}function App() {const [count, setCount] = useState(0);return (<div><button onClick={() => setCount(count + 1)}>App组件的按钮,count={count}</button><Box></Box></div>)
}

    点击父组件之后,子组件中的随机数也会进行刷新,但是其实父组件中其实没有明确需要更新子组件的内容。
在这里插入图片描述
    但是需要注意的是这里useSate能够缓存初始值,虽然能够随着父组件变化触发子组件中p标签的Math.random()函数,但是count的属性值是不会改变的,也就是没有重新触发子组件中useState相关的操作。
    下面介绍一下useMemo的用法,useMemo接受两个参数一个是回调函数,返回需要被缓存的结果,另外一个是绑定的对象列表,如果该值变化那么useMemo中的回调函数会被重新执行:

function Box() {const [count, setCount] = useState(Math.random());const testData = useMemo(() => Math.random(), []);return (<div><p>随机数:{testData}</p><button onClick={() => setCount(count + 1)}>Box子组件的按钮,count={count}</button></div>)
}

在这里插入图片描述
    应用场景就比如缓存网络接口请求的返回结果,这样能够减少请求次数,提高性能。还有一种场景,可以模拟computed属性,通过监听多个状态对象(和Vue的computed一致)。
    useCallback和useMemo类似,能够返回一个缓存函数。和useMemo的关系可以理解成:

useMemo(()=>fn,deps);//等价于
useCallback(fn,deps)();

五、useRef处理ref对象

    useRef 是 React 提供的一个 Hook,主要用于在组件的多次渲染之间存储一个可变的引用值。它返回一个可变的 ref 对象,其 .current 属性被初始化为传入的参数,这个 ref 对象在组件的整个生命周期内保持不变,即可以随便修改.current属性,但是在组件刷新的时候不能够修改触发useRef的重新渲染。关于ref在React Ref引用机制解析中有详细介绍。

六、useContext共享状态

    在提及这个问题之前,首先需要明确react同级组件共享同一个属性是怎么实现,下面直接给出演示代码:
在这里插入图片描述
    通过createContext机制可以实现组件共享一个state值。但是这种写法不是特别方便,需要全局提供createContext的绑定,之后还需要使用provider和consumer组件的传递模式,代码嵌套层级较深,显得有些冗余。因此出现了useContext方法,简化成如下写法:
在这里插入图片描述

七、useReducer优化组件

    useReducer 是 useState 的升级方案,用于管理复杂的状态逻辑。与 useState 不同,useReducer 使用一个 reducer 函数来处理状态更新,使状态管理更加可预测和结构化。常见用法如下:

const initialState = 10;
function reducer(state: any, action: any) {const {type,val}=action;const newState = { ...state };switch (type) {case 'add': {newState.num += val; break;}case 'sub': {newState.num -= val; break;}default: ;}return newState;
}
function init(initialState: any) {return {num: initialState};
}
function App() {const [state, dispatch] = useReducer(reducer, initialState, init);return (<div><div><button className='btn' onClick={() => dispatch({ type: 'add', val: 1 })}>点击+1</button></div><div>输出当前的值:num={state.num}</div><div><button className='btn' onClick={() => dispatch({ type: 'sub', val: 1 })}>点击-1</button></div></div>)
}

    useReducer接受三个参数,分别是用于处理更新state的函数,初始值,用于对初始值进行封装的函数。使用这种更新方式之后,能够将state的管理与原来的管理方式分离,提高复用性。比如不同的state值都需要进行增加或者减少,可以复用上面的reducer函数。另外useReducer 还可以和useContext组件配合使用,和useContext里面GlobalContext.Provider向子组件传值一样,这里可以将reducer生成的dispatch函数传递给子组件共享,也能实现子组件状态共享和同时维护:
在这里插入图片描述

八、自定义组件

    当我们在多个函数组件中存在相似的状态逻辑(例如处理网络请求、管理定时器等)时,如果重复编写这些逻辑,会导致代码冗余。自定义 Hook 提供了一种将这些可复用的逻辑提取出来的方式,提高代码的可维护性和复用性。比如以上面天气请求为例,封装里面的input的请求体,可以得到如下代码:
在这里插入图片描述
    这里代码可能会提示请求次数过高,这里是因为input本身监听的问题,可以设置防抖函数来处理,或者使用真实密钥,这里只是测试数据,所以没有对QPS优化。

http://www.xdnf.cn/news/3869.html

相关文章:

  • 快速外网访问,证书自动续约 | 极空间IPv4IPv6 DDNS 配置详解
  • 数据结构与算法:回溯
  • Python:Seaborn 美化图表的技术指南
  • 【五一培训】Day 4
  • 常用命令集合
  • PCB叠层设计方案
  • 探秘DeepSeek模型参数:解锁AI潜能的密码
  • GenCLS++:通过联合优化SFT和RL,提升生成式大模型的分类效果
  • Python之学习笔记(六)
  • Prompt compress 技术探究-LLMLingua
  • SpringAi接入DeepSeek大模型
  • SurfSense开源程序是NotebookLM / Perplexity / Glean的开源替代品,连接到外部来源,如搜索引擎
  • ArrayList的扩容机制(源码解析)
  • 深度学习的简单介绍
  • PISI:眼图1:眼图相关基本概念
  • 使用synchronized关键字同步Java线程
  • AndroidLogger常用命令和搜索功能介绍
  • STM32Cube-FreeRTOS任务调度与任务管理-笔记
  • ruoyi-flowable框架关于启动时提示锁住问题
  • LLM论文笔记 27: Looped Transformers for Length Generalization
  • n8n工作流自动化平台的实操:利用本地嵌入模型,完成文件内容的向量化及入库
  • 【Linux网络#3】:Socket编程应用层UDP
  • Scartch038(四季变换)
  • MCP智能体多Agent协作系统设计(Multi-Agent Cooperation)
  • 模型部署——cuda编程入门
  • C语言内存函数详解:从基础到实战
  • 2025年渗透测试面试题总结-拷打题库38(题目+回答)
  • profile软件开发中的性能剖析与内存分析
  • 数据库Mysql_联合查询
  • Python----机器学习(模型评估:准确率、损失函数值、精确度、召回率、F1分数、混淆矩阵、ROC曲线和AUC值、Top-k精度)