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 包在内部用,
顶层调用是铁则!

相关推荐
前端小巷子8 分钟前
深入 npm 模块安装机制
前端·javascript·面试
深职第一突破口喜羊羊1 小时前
记一次electron开发插件市场遇到的问题
javascript·electron
cypking1 小时前
electron中IPC 渲染进程与主进程通信方法解析
前端·javascript·electron
西陵1 小时前
Nx带来极致的前端开发体验——借助playground开发提效
前端·javascript·架构
江城开朗的豌豆2 小时前
Element UI动态组件样式修改小妙招,轻松拿捏!
前端·javascript·vue.js
float_六七2 小时前
JavaScript:现代Web开发的核心动力
开发语言·前端·javascript
zhaoyang03012 小时前
vue3笔记(2)自用
前端·javascript·笔记
UrbanJazzerati3 小时前
JavaScript Promise完整指南
javascript
德育处主任Pro3 小时前
# JsSIP 从入门到实战:构建你的第一个 Web 电话
前端
拾光拾趣录3 小时前
setTimeout(1) 和 setTimeout(2) 的区别
前端·v8