useEffect
useEffect
是 React 中的一个 Hook(钩子),用于在函数组件中处理副作用(side effects)。副作用指的是那些在渲染之外的操作,比如数据请求、订阅事件、手动操作 DOM 等。在类组件中,这些逻辑通常写在生命周期方法(如 componentDidMount
、componentDidUpdate
、componentWillUnmount
)中,而 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>;
}
代码解析
useEffect(() => {}, [])
:- 第一个参数是一个函数,定义副作用逻辑。
- 第二个参数是依赖数组(dependency array),决定副作用何时执行。
依赖数组的作用:
[]
(空数组):副作用只在组件首次渲染(挂载)时运行一次,类似componentDidMount
。[data]
(有依赖):每次data
改变时,副作用都会重新运行,类似componentDidUpdate
。- 省略依赖数组:副作用在每次渲染后都运行(不推荐,可能导致性能问题)。
清理函数:
- 如果返回一个函数,React 会在组件卸载(或下次副作用运行前)时调用它,类似
componentWillUnmount
。
- 如果返回一个函数,React 会在组件卸载(或下次副作用运行前)时调用它,类似
常见使用场景
数据获取:
useEffect(() => { async function fetchData() { const response = await fetch('https://api.example.com/data'); const result = await response.json(); setData(result); } fetchData(); }, []); // 只在挂载时请求一次
事件监听:
useEffect(() => { const handleResize = () => console.log(window.innerWidth); window.addEventListener('resize', handleResize); // 清理:移除监听器 return () => window.removeEventListener('resize', handleResize); }, []); // 只在挂载和卸载时运行
依赖状态更新:
const [count, setCount] = useState(0); useEffect(() => { document.title = `点击了 ${count} 次`; }, [count]); // 每次 count 变化时更新标题
与 Next.js 的关系
在 Next.js 中,useEffect
的用法与普通 React 一致,但需要注意以下几点:
- 服务端渲染 (SSR):
useEffect
只在客户端运行。如果在 SSR 中需要初始数据,建议使用getServerSideProps
或getStaticProps
,而不是依赖useEffect
。 - hydration:在 Next.js 的 SSR 或 SSG 场景中,
useEffect
会在客户端 hydration 完成后执行,适合处理客户端特有的逻辑。
关键点
- 运行时机:
useEffect
在组件渲染完成后异步执行,不会阻塞 UI 更新。
- 依赖管理:
- 依赖数组中必须包含副作用中使用到的所有外部变量,否则可能导致 bug。
- 使用 ESLint 插件(如
eslint-plugin-react-hooks
)可以帮助检测遗漏的依赖。
- 清理副作用:
- 如果副作用涉及订阅或定时器(如
setInterval
),务必在清理函数中取消,以避免内存泄漏。
- 如果副作用涉及订阅或定时器(如
对比类组件生命周期
类组件生命周期 | useEffect 等价用法 |
---|---|
componentDidMount |
useEffect(() => {}, []) |
componentDidUpdate |
useEffect(() => {}, [dep]) |
componentWillUnmount |
useEffect(() => { return () => {} }, []) |
调试技巧
- 如果
useEffect
运行次数不符合预期,检查依赖数组是否正确。 - 使用
console.log
确认副作用和清理函数的执行时机。
useState
useState
是 React 中的一个 Hook(钩子),用于在函数组件中添加和管理状态(state)。在 React 的类组件中,状态通常通过 this.state
和 this.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>
);
}
代码解析
useState(0)
:useState
接受一个参数作为状态的初始值(这里是0
)。- 返回一个数组,包含两个元素:
- 第一个元素(
count
):当前状态的值。 - 第二个元素(
setCount
):一个函数,用于更新状态。
- 第一个元素(
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 中,初始状态需要在服务器端正确初始化(通常配合 getServerSideProps
或 useEffect
)。
常见问题
为什么不能在条件语句中使用
useState
?- React 依赖 Hook 的调用顺序来追踪状态。如果在条件中调用
useState
,顺序可能变化,导致状态管理混乱。
- React 依赖 Hook 的调用顺序来追踪状态。如果在条件中调用
初始值只生效一次:
useState
的初始值只在组件首次渲染时使用,后续更新只依赖setState
。