深入探索 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 提供了一种强有力的方式来增强我们的应用开发流程。

相关推荐
lichenyang4531 分钟前
Axios封装以及添加拦截器
前端·javascript·react.js·typescript
Trust yourself24316 分钟前
想把一个easyui的表格<th>改成下拉怎么做
前端·深度学习·easyui
三口吃掉你21 分钟前
Web服务器(Tomcat、项目部署)
服务器·前端·tomcat
Trust yourself24323 分钟前
在easyui中如何设置自带的弹窗,有输入框
前端·javascript·easyui
烛阴27 分钟前
Tile Pattern
前端·webgl
前端工作日常1 小时前
前端基建的幸存者偏差
前端·vue.js·前端框架
Electrolux1 小时前
你敢信,不会点算法没准你赛尔号都玩不明白
前端·后端·算法
天若有情6732 小时前
【技术新闻】OpenAI发布GPT-5,AI编程助手迎来革命性突破
gpt·ai编程·业界资讯·新闻资讯
a cool fish(无名)2 小时前
rust-参考与借用
java·前端·rust
Feather_742 小时前
从Taro的Dialog.open出发,学习远程控制组件之【事件驱动】
javascript·学习·taro