useState和useEffect

半兽人 发表于: 2025-03-11   最后更新时间: 2025-03-11 11:27:03  
{{totalSubscript}} 订阅, 91 游览

useEffect

useEffect 是 React 中的一个 Hook(钩子),用于在函数组件中处理副作用(side effects)。副作用指的是那些在渲染之外的操作,比如数据请求、订阅事件、手动操作 DOM 等。在类组件中,这些逻辑通常写在生命周期方法(如 componentDidMountcomponentDidUpdatecomponentWillUnmount)中,而 useEffect 则将这些功能统一到了函数组件中。

基本概念

  • 副作用:指组件渲染完成后需要执行的操作,例如:
    • 从 API 获取数据。
    • 添加事件监听器。
    • 更新浏览器标题。
  • useEffect 允许你在组件渲染后执行这些操作,并且可以选择性地清理副作用(比如取消订阅)。

用法

import React, { useState, useEffect } from 'react';

function Example() {
  const [data, setData] = useState(null);

  // useEffect 的基本结构
  useEffect(() => {
    // 副作用代码:比如获取数据
    fetch('https://api.example.com/data')
      .then((response) => response.json())
      .then((result) => setData(result));

    // 可选:返回一个清理函数
    return () => {
      console.log('清理副作用');
    };
  }, []); // 依赖数组

  return <div>{data ? data.message : '加载中...'}</div>;
}

代码解析

  1. useEffect(() => {}, [])

    • 第一个参数是一个函数,定义副作用逻辑。
    • 第二个参数是依赖数组(dependency array),决定副作用何时执行。
  2. 依赖数组的作用

    • [](空数组):副作用只在组件首次渲染(挂载)时运行一次,类似 componentDidMount
    • [data](有依赖):每次 data 改变时,副作用都会重新运行,类似 componentDidUpdate
    • 省略依赖数组:副作用在每次渲染后都运行(不推荐,可能导致性能问题)。
  3. 清理函数

    • 如果返回一个函数,React 会在组件卸载(或下次副作用运行前)时调用它,类似 componentWillUnmount

常见使用场景

  1. 数据获取

    useEffect(() => {
    async function fetchData() {
     const response = await fetch('https://api.example.com/data');
     const result = await response.json();
     setData(result);
    }
    fetchData();
    }, []); // 只在挂载时请求一次
    
  2. 事件监听

    useEffect(() => {
    const handleResize = () => console.log(window.innerWidth);
    window.addEventListener('resize', handleResize);
    
    // 清理:移除监听器
    return () => window.removeEventListener('resize', handleResize);
    }, []); // 只在挂载和卸载时运行
    
  3. 依赖状态更新

    const [count, setCount] = useState(0);
    useEffect(() => {
    document.title = `点击了 ${count} 次`;
    }, [count]); // 每次 count 变化时更新标题
    

与 Next.js 的关系

在 Next.js 中,useEffect 的用法与普通 React 一致,但需要注意以下几点:

  • 服务端渲染 (SSR)useEffect 只在客户端运行。如果在 SSR 中需要初始数据,建议使用 getServerSidePropsgetStaticProps,而不是依赖 useEffect
  • hydration:在 Next.js 的 SSR 或 SSG 场景中,useEffect 会在客户端 hydration 完成后执行,适合处理客户端特有的逻辑。

关键点

  1. 运行时机
    • useEffect 在组件渲染完成后异步执行,不会阻塞 UI 更新。
  2. 依赖管理
    • 依赖数组中必须包含副作用中使用到的所有外部变量,否则可能导致 bug。
    • 使用 ESLint 插件(如 eslint-plugin-react-hooks)可以帮助检测遗漏的依赖。
  3. 清理副作用
    • 如果副作用涉及订阅或定时器(如 setInterval),务必在清理函数中取消,以避免内存泄漏。

对比类组件生命周期

类组件生命周期 useEffect 等价用法
componentDidMount useEffect(() => {}, [])
componentDidUpdate useEffect(() => {}, [dep])
componentWillUnmount useEffect(() => { return () => {} }, [])

调试技巧

  • 如果 useEffect 运行次数不符合预期,检查依赖数组是否正确。
  • 使用 console.log 确认副作用和清理函数的执行时机。

useState

useState 是 React 中的一个 Hook(钩子),用于在函数组件中添加和管理状态(state)。在 React 的类组件中,状态通常通过 this.statethis.setState 来管理,而 useState 则为函数组件提供了类似的功能,让开发者可以在不使用类的情况下处理动态数据。

基本概念

  • 状态(state):指的是组件中会随着时间变化的数据,例如用户的输入、点击次数、是否显示某个元素等。
  • useState 允许你声明一个状态变量,并提供一个方法来更新它。

用法

import React, { useState } from 'react';

function Example() {
  // 声明状态变量 count,初始值为 0
  const [count, setCount] = useState(0);

  return (
    <div>
      <p>你点击了 {count} 次</p>
      <button onClick={() => setCount(count + 1)}>点击我</button>
    </div>
  );
}

代码解析

  1. useState(0)

    • useState 接受一个参数作为状态的初始值(这里是 0)。
    • 返回一个数组,包含两个元素:
      • 第一个元素(count):当前状态的值。
      • 第二个元素(setCount):一个函数,用于更新状态。
  2. setCount

    • 调用 setCount 会更新 count 的值,并触发组件重新渲染。
    • 它可以接受新值,或者一个基于旧值的函数(如 setCount(prevCount => prevCount + 1))。

特点

  • 局部状态useState 定义的状态只属于当前组件,不会影响其他组件。
  • 函数式更新:如果新状态依赖于旧状态,推荐使用函数形式(如 setCount(prev => prev + 1)),以避免潜在的更新冲突。
  • 可以管理多种类型:初始值可以是数字、字符串、对象、数组等。

更复杂的例子

管理对象状态:

import React, { useState } from 'react';

function Form() {
  const [user, setUser] = useState({ name: '', age: 0 });

  return (
    <div>
      <input
        value={user.name}
        onChange={(e) => setUser({ ...user, name: e.target.value })}
      />
      <input
        value={user.age}
        onChange={(e) => setUser({ ...user, age: Number(e.target.value) })}
      />
      <p>姓名: {user.name}, 年龄: {user.age}</p>
    </div>
  );
}
  • 注意:更新对象或数组时,需要用扩展运算符(...)保留未更改的部分,否则会覆盖整个状态。

与 Next.js 的关系

在 Next.js 中,useState 的使用与普通 React 项目完全相同,因为 Next.js 是基于 React 的框架。无论是在客户端渲染、SSR 还是 SSG 的场景下,useState 都可以用来管理组件的交互状态。不过,在 SSR 中,初始状态需要在服务器端正确初始化(通常配合 getServerSidePropsuseEffect)。

常见问题

  1. 为什么不能在条件语句中使用 useState

    • React 依赖 Hook 的调用顺序来追踪状态。如果在条件中调用 useState,顺序可能变化,导致状态管理混乱。
  2. 初始值只生效一次

    • useState 的初始值只在组件首次渲染时使用,后续更新只依赖 setState
更新于 2025-03-11
在线,1小时前登录

查看Next.js更多相关的文章或提一个关于Next.js的问题,也可以与我们一起分享文章