React Hooks 生存指南:让你的函数组件"活"起来 🧬

友情提示:阅读本文时请确保你的大脑内存未被 useState 占用,否则可能导致 useEffect 依赖项紊乱。

第一章:初识魔法棒------useState ✨

想象一下,你是个函数组件世界的魔法学徒,而 useState 就是你的第一根魔法棒。它能让你在函数组件中召唤状态精灵

useState是什么呢?

useState 是 React 中的一个 Hook,它使得函数组件可以拥有自己的状态(state)。在使用类组件时,我们通常需要通过 this.statethis.setState 来管理组件的状态。而在函数组件中,由于没有实例(即没有 this),因此引入了 Hooks 的概念来解决这个问题,useState 就是其中之一。

基本用法

javascript 复制代码
import React, { useState } from 'react';

function Example() {
  // 声明一个新的叫做 "count" 的 state 变量
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>
        Click me
      </button>
    </div>
  );
}

在这个例子中,useState(0) 创建了一个名为 count 的状态变量,并将其初始值设置为 0setCount 函数用于更新这个状态。这时候我们数据发生改变,我们页面的数据也会被驱动改变,和原生JS不同,我们不需要去操作DOM

优点

  1. 简化状态管理 :无需编写冗长的类构造函数或使用 this 关键字,让代码更加简洁。
  2. 增强可读性与维护性 :每个 useState 调用都是独立且清晰的,易于理解和维护。
  3. 更灵活的状态逻辑 :允许你在不改变组件层次结构的情况下复用状态逻辑,特别是在结合其他 Hooks(如 useEffect)时。
  4. 更好的性能优化:React 对函数组件和 Hooks 进行了优化,能够更好地处理组件的重新渲染,提高应用性能。
  5. 促进模块化设计:可以使功能更模块化、更可重用,有助于构建大型应用程序。

第二章:时空管理者------useEffect 🕰️

如果说 useState 是魔法棒,那么 useEffect 就是你的时间管理器。它专门处理那些"不该在渲染时捣乱"的副作用任务。 React 官方文档中将副作用定义为:

"任何在渲染之后需要执行的操作。"

你可以把它理解为类组件生命周期方法的组合替代

2.1 基础生存法则

javascript 复制代码
useEffect(() => {
  console.log('我像幽灵一样每次渲染后都出现')
}) // 没有依赖项 = 阴魂不散

一但页面重新渲染,就会执行,比如我们上诉用useState改变页面的数据

2.2 挂载模式:[] 依赖项

scss 复制代码
useEffect(() => {
  console.log('只在组件挂载时运行一次!!!')
  fetchRepos() // 趁现在赶紧偷数据!
}, []) // 空数组 = 仅出生时执行

这就像给新生儿办出生证明------只执行一次的重要仪式。只在页面刚刚渲染完成时执行

2.3 追踪模式:监视特定状态

scss 复制代码
useEffect(() => {
  console.log('count变了!快通知监控中心!')
}, [count]) // 监视count精灵的异动

这相当于给你的状态精灵安装了 GPS 追踪器。一但count改变,就会执行useEffect里面的逻辑,当然我们也可以同时监视多个数据

useEffect 的优点

优点 说明
统一副作用管理 将所有副作用集中处理,避免分散在多个生命周期中,提高可维护性。
组件解耦更清晰 不再需要在多个生命周期中来回跳转,副作用逻辑更容易理解和测试。
自动处理依赖更新 通过依赖数组 [deps] 控制执行时机,避免不必要的重复调用。
支持清理机制 可以返回一个函数用于清理副作用,防止内存泄漏。
复用逻辑更方便 可以封装成自定义 Hook,在多个组件之间共享副作用逻辑。

第三章:生命周期大逃杀 ♻️

类组件的生命周期在函数组件中化身三个 useEffect 形态:

生命周期 useEffect 形态 典型用途
Mounted useEffect(fn, []) 初始化数据、开启定时器
Updated useEffect(fn, [dep]) 依赖项变化时更新
Unmount return cleanup 清理定时器、取消请求

在 Timer 组件中,我们完美展示了这个循环:

scss 复制代码
useEffect(() => {
  const interval = setInterval(() => { 
    setTime(prev => prev + 1) // 定时器:时间刺客
  }, 1000)
  
  return () => { // 卸载时的清理小队
    clearInterval(interval) // 消灭时间刺客!
    console.log('组件卸载')
  }
}, []) // 出生时召唤刺客,死亡时消灭刺客

我们设置了一个定时器,也就是查看页面运行多久,当我们在根组件,修改这个Timer状态

xml 复制代码
{isTimerOn && <Timer />}
      <button onClick={() => {
        setTisTimerOn(!isTimerOn)
      }}>toogle timer</button>

这样我们的根组件就重新渲染,就会执行我们的useEffect,卸载组件,否则还是会继续执行

第四章:API 请求的黄金时机 ⏳

组件世界最重要的哲学问题:何时请求数据?

答案藏在 根 组件的这个片段:

scss 复制代码
useEffect(() => {
  const fetchRepos = async () => {
    const response = await fetch('https://api.github.com/...')
    setRepos(await response.json()) // 偷到数据塞给仓库精灵
  }
  fetchRepos()
}, []) // 挂载时偷一次就够了

这里我们直接在页面渲染完成后就执行API的请求,而且由于是[]依赖项,我们只在初次组件渲染请求,很好的满足了我们的要求

为什么是 useEffect 而不是直接放在函数体里?

  • 避免阻塞渲染:请求是异步操作,不挡路
  • 避免重复请求:依赖项 [] 保证只偷一次
  • 避免内存泄漏:配合清理函数更安全

还有为什么async不能写在useEffect这个函数上呢?

错误原因:

  • useEffect 要求传入的函数要么返回一个清理函数(function),要么返回 undefined
  • 如果你把 useEffect 的回调写成 async 函数,它会返回一个 Promise,而不是 undefined 或函数。
  • React 不知道如何处理这个 Promise,所以会警告或导致副作用行为异常。

第六章:精灵动物园实战 🦁

看看 App 组件如何管理多个状态精灵:

scss 复制代码
function App() {
  // 精灵召唤仪式
  const [count, setCount] = useState(0)
  const [num, setNumber] = useState(0)
  const [repos, setRepos] = useState([])
  const [isTimerOn, setTisTimerOn] = useState(true)

  // 监视精灵们的特殊效果
  useEffect(() => console.log('count变啦!'), [count])
  useEffect(() => console.log('num变啦!'), [num])
  useEffect(() => console.log('有精灵动了!'), [count, num])

  // 组件卸载时的彩蛋
  return (
    <>
      {isTimerOn && <Timer />}
      <button onClick={() => setTisTimerOn(!isTimerOn)}>
        {isTimerOn ? '封印计时器' : '解封计时器'}
      </button>
    </>
  )
}

点击按钮时触发计时器组件的生与死:

javascript 复制代码
// Timer被卸载时执行清理函数
return () => {
  console.log('组件卸载')
  clearInterval(interval) // 必须清理!
}

第七章:Hooks 生存法则 📜

  1. 精灵召唤法则:只能在函数组件顶部调用 Hook
  2. 依赖诚实原则:useEffect 依赖项必须诚实交代所有用到的状态
  3. 清理义务:有开启必有清理(定时器、订阅等)
  4. 异步隔离原则:async 函数必须在 useEffect 内部定义执行
  5. 精灵独立宣言:每个状态都有独立存储空间

结语:函数组件的重生 🦋

当 Timer 组件中的 console.log('组件函数执行') 每次渲染都被调用时,useEffect 里的逻辑却像经过精密设计的定时炸弹------只在正确时机引爆。这种"函数体执行 ≠ 副作用执行"的特性,正是 Hooks 的精妙之处。

最后赠送 Hook 修炼心法口诀:
状态用 useState,
副作用靠 Effect。
依赖数组要诚实,
清理函数不能缺。
Async 包在内部用,
顶层调用是铁则!

相关推荐
脑袋大大的31 分钟前
JavaScript 性能优化实战:减少 DOM 操作引发的重排与重绘
开发语言·javascript·性能优化
速易达网络2 小时前
RuoYi、Vue CLI 和 uni-app 结合构建跨端全家桶方案
javascript·vue.js·低代码
耶啵奶膘2 小时前
uniapp+firstUI——上传视频组件fui-upload-video
前端·javascript·uni-app
JoJo_Way2 小时前
LeetCode三数之和-js题解
javascript·算法·leetcode
视频砖家3 小时前
移动端Html5播放器按钮变小的问题解决方法
前端·javascript·viewport功能
lyj1689973 小时前
vue-i18n+vscode+vue 多语言使用
前端·vue.js·vscode
小白变怪兽4 小时前
一、react18+项目初始化(vite)
前端·react.js
ai小鬼头4 小时前
AIStarter如何快速部署Stable Diffusion?**新手也能轻松上手的AI绘图
前端·后端·github
墨菲安全5 小时前
NPM组件 betsson 等窃取主机敏感信息
前端·npm·node.js·软件供应链安全·主机信息窃取·npm组件投毒