深入探索 React Hooks: useState 与 useEffect 的力量 🌟

前言

之前有讲过如何从0开始入门React,今天我们来进阶一下,学习React 的钩子:useState、useEffect 。本篇文章结合 原生JS 来讲解。

一、useState

在 React 中,useState 是一个用于给函数组件添加状态的 Hook。它允许你在不编写类的情况下使用状态这一概念。

1.1 原生JS修改状态

假设我们需要创建一个简单的计数器功能,使用原生 JavaScript 实现如下:

js 复制代码
function createState(initialState) {
  let state = initialState;

  function getState() {
    return state;
  }

  function setState(newState) {
    state = newState;
    render(); // 模拟状态更新后的重新渲染
  }

  function render() {
    console.log(`当前状态: ${state}`);
  }
  return { getState, setState };
}

// 创建一个计数器状态实例
const counter = createState(0);
console.log(counter.getState()); // 输出: 当前状态: 0
counter.setState(1); // 输出: 当前状态: 1
console.log(counter.getState()); // 输出: 当前状态: 1

在这个例子中,我们定义了一个 createState 函数,它接受一个初始状态作为参数,并返回两个方法:getStatesetStategetState 方法用于获取当前的状态值,而 setState 方法用于更新状态并触发一次"重新渲染"(这里通过调用 render 函数模拟)。

1.2 useState响应式更新

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

function ExampleComponent() {
  const [count, setCount] = useState(0);

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

在这个例子中,我们初始化了一个名为 count 的状态变量,并设置其初始值为 0。setCount 函数用于更新这个状态。

对比原生js可以看出 useState 提供了多项优势:

响应式更新

  • 自动重新渲染 :当使用 useState 更新状态时,React 会自动检测到这些变化,并触发组件的重新渲染。这与手动使用原生 JavaScript 更新 DOM 相比,极大地简化了状态管理和UI同步的过程。
  • 减少副作用:在原生 JavaScript 中,你需要自己管理DOM更新的逻辑,包括何时以及如何更新。而 React 通过其声明式的编程模型帮助你减少了这种复杂性。

二、useEffect

React 组件也有"出生、长大、死亡"的过程,useEffect 是控制这些阶段的关键工具,你可以用它来模拟原生中的各种行为(比如加载完执行代码、卸载前清理)。

2.1 原生 JS 中的"生命周期"是什么?

我们先回顾一下原生 JS:

html 复制代码
<div id="app"></div>

<script>
  // 创建一个元素
  const div = document.createElement('div');
  div.innerText = 'Hello';
  
  // 添加到页面上(挂载)
  document.getElementById('app').appendChild(div);
  // 后续可能会修改内容(更新)
  div.innerText = 'World';
  // 最后可能移除它(卸载)
  div.remove();
</script>

在这个过程中,我们能观察到几个关键阶段:

  1. 创建元素 → 类似于组件被创建
  2. 添加到页面中 → 组件挂载(mounted)
  3. 内容发生变化 → 组件更新(updated)
  4. 从页面中移除 → 组件卸载(unmounted)

在 React 中,组件也有类似的过程,叫做"生命周期"。

2.2 React 函数组件的生命周期 -> 类比原生JS

2.2.1 挂载阶段(Mount)→ 第一次显示

类比:把新元素插入到 DOM 中

jsx 复制代码
function App() {
  console.log("组件开始执行");
  
  return <div>Hello, React!</div>;
}

App 组件第一次被渲染时,函数就会执行。 就像在原生里写了一个 document.createElement() 并把它插入到了页面中。

2.2.2 首次渲染完成(Did Mount)→ 已经显示出来了

类比:元素已经出现在页面上,可以做些操作了

jsx 复制代码
useEffect(() => {
  console.log("组件首次渲染完成");
}, []);

这个时候你可以操作 DOM、发送请求、设置定时器等。 类似于在原生中写 document.getElementById(...) 然后修改样式或绑定事件。

2.2.3 更新阶段(Update)→ 数据变了,界面要刷新

类比:DOM 内容发生了变化

jsx 复制代码
const [count, setCount] = useState(0);

<button onClick={() => setCount(count + 1)}>点我</button>
<p>当前计数:{count}</p>

每次点击按钮,count 变了,组件会重新执行(整个函数重新运行一遍 )页面上的数字也会更新,就像在原生里写了 element.innerText = newValue

2.2.4 清理副作用(Will Unmount)→ 要离开页面前做一些清理

类比:删除定时器、取消

jsx 复制代码
useEffect(() => {
  const timer = setInterval(() => {
    console.log("定时器运行中...");
  }, 1000);

  return () => {
    clearInterval(timer); // 组件卸载前清除定时器
    console.log("组件即将卸载");
  };
}, []);

这个返回的函数会在组件被销毁前调用 。类似于在原生中做了 timerId && clearInterval(timerId)防止内存泄漏

React 生命周期总结

React 生命周期阶段 对应原生行为 Hook 写法
挂载(Mount) 元素被插入 DOM 组件函数执行
首次渲染完成(DidMount) 元素已插入成功,可操作 DOM useEffect(() => {}, [])
更新(Update) 数据改变,视图更新 修改 state,组件重新执行
卸载(Unmount) 元素被移除 useEffect(() => { return () => {} }, [])

2.3 依赖数组

依赖数组控制着 useEffect 内部逻辑何时执行 。如果提供了依赖项(变量或状态),当这些依赖项发生改变 时,useEffect 将会重新运行

jsx 复制代码
useEffect(() => {
  console.log('effect triggered');
}, [count]);

在这个例子中,每当 count 变化时,效果就会被触发。如果没有提供依赖项或者依赖项为空数组 [],那么这个 useEffect 只会在组件挂载和卸载的时候运行一次

2.4 异步函数处理

React 期望 useEffect 的返回值要么是 undefined(即不返回任何东西),要么是一个清理函数

jsx 复制代码
useEffect(() => {
  // 要重新声明一个异步函数,不可以直接在useEffect的回调函数中使用
  const fetchData = async () => {
    try {
      const response = await fetch('your-api-url');
      const data = await response.json();
      // 使用数据更新组件状态
    } catch (error) {
      console.error("Error fetching data: ", error);
    }
  };

  fetchData();
}, []); // 空数组意味着仅在组件挂载和卸载时运行

如果尝试直接将 useEffect 的回调定义为异步函数(async 函数),它会隐式地返回一个** Promise**,而不是预期的函数或 undefined,这就是为什么不能直接在 useEffect 中使用 async 函数的原因。

总结

通过探讨 useState 和 useEffect,我们看到了 React 如何简化状态管理和副作用处理,使得代码更加清晰、易于维护。无论是响应式的数据更新还是生命周期管理,Hooks 提供了一种强有力的方式来增强我们的应用开发流程。

相关推荐
rzl029 分钟前
java web5(黑马)
java·开发语言·前端
Amy.Wang10 分钟前
前端如何实现电子签名
前端·javascript·html5
海天胜景12 分钟前
vue3 el-table 行筛选 设置为单选
javascript·vue.js·elementui
今天又在摸鱼13 分钟前
Vue3-组件化-Vue核心思想之一
前端·javascript·vue.js
蓝婷儿15 分钟前
每天一个前端小知识 Day 21 - 浏览器兼容性与 Polyfill 策略
前端
百锦再17 分钟前
Vue中对象赋值问题:对象引用被保留,仅部分属性被覆盖
前端·javascript·vue.js·vue·web·reactive·ref
win4r20 分钟前
🚀 SuperClaude让Claude Code编程能力暴增300%!小白秒变顶尖程序员!19个专业命令+9大预定义角色,零编程经验也能开发复杂项目,完全碾
aigc·ai编程·claude
jingling55521 分钟前
面试版-前端开发核心知识
开发语言·前端·javascript·vue.js·面试·前端框架
拾光拾趣录26 分钟前
CSS 深入解析:提升网页样式技巧与常见问题解决方案
前端·css