在 React Native 的开发中,函数式组件 是目前的主流选择。而在函数式组件里,最常用的 Hook 之一就是 useEffect
。它的作用简单来说就是:在函数组件中处理副作用(Side Effects)。
那么什么是副作用呢?比如下面的这些:
- 向服务器发送请求获取数据;
- 订阅一个事件(如监听键盘或网络状态变化);
- 操作本地存储;
- 直接操作 DOM 或 Native UI 元素;
- 设置定时器。
这些逻辑都不是单纯地根据 props 和 state 来渲染 UI,所以在 React Native 中称它们为副作用,需要放到 useEffect
里去执行。
首先,我们来看下它的基本用法。
useEffect 的基本用法
useEffect
的语法如下:
tsx
useEffect(() => {
// 副作用逻辑,例如请求数据、打印日志等
doSomething();
// 可选:返回一个清理函数
return () => {
clearSomething();
};
}, [依赖项数组]);
通过上面的代码我们可以看到,useEffect
函数接受两个参数:
- 第一个参数:一个函数,里面写副作用逻辑。
- 返回值(可选,有需要就传):一个清理函数,在组件卸载或依赖项变化之前执行。
- 第二个参数(依赖项数组):控制
useEffect
什么时候执行。
接着,我们来看下依赖项数据有哪些情况。
依赖项数组的几种情况
不写依赖项
tsx
useEffect(() => {
console.log("组件每次渲染都会执行");
});
如果你没有写依赖项,那么只要组件更新(包括 state 或 props 变化),都会重新执行 useEffect
。
Tips:这种写法一般不推荐,除非你确实需要"每次渲染都执行"。
依赖项数组为空
tsx
useEffect(() => {
console.log("只在第一次渲染时执行,相当于 componentDidMount");
return () => {
console.log("组件卸载时执行,相当于 componentWillUnmount");
};
}, []);
这时,useEffect
只会在 组件挂载时执行一次,适合用来:
- 请求初始化数据;
- 注册全局事件监听;
- 启动定时器。
并且,当组件卸载时,清理函数会执行,用来取消监听或清除定时器。
依赖项数组中有变量(最常用的使用方式)
tsx
useEffect(() => {
console.log("当 count 变化时才会执行这个副作用");
}, [count]);
当 count
变化时,useEffect
会执行。这种场景非常常见,例如依赖某个 state 去请求数据。
常见使用场景
请求数据
比如我们想在页面初始化时请求数据:
tsx
import React, { useEffect, useState } from "react";
import { View, Text, ActivityIndicator } from "react-native";
export default function UserList() {
const [users, setUsers] = useState<string[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
// 模拟请求数据
setTimeout(() => {
setUsers(["Alice", "Bob", "Charlie"]);
setLoading(false);
}, 2000);
}, []);
if (loading) {
return <ActivityIndicator size="large" color="#0000ff" />;
}
return (
<View>
{users.map((user, index) => (
<Text key={index}>{user}</Text>
))}
</View>
);
}
这里我们在 useEffect
中发送"模拟请求",组件挂载时请求数据并更新 UI。
定时器
tsx
import React, { useEffect, useState } from "react";
import { View, Text } from "react-native";
export default function TimerDemo() {
const [time, setTime] = useState(0);
useEffect(() => {
const timer = setInterval(() => {
setTime((prev) => prev + 1);
}, 1000);
return () => clearInterval(timer); // 清理定时器
}, []);
return (
<View>
<Text>计时器:{time} 秒</Text>
</View>
);
}
如果没有清理定时器,组件卸载后还会继续执行,造成 bug。
避免首次执行
有时我们希望 useEffect
依赖项变化时才执行 ,但是不想在第一次渲染时执行。 这时可以用一个标记变量:
tsx
import React, { useEffect, useRef, useState } from "react";
import { View, Text, Button } from "react-native";
export default function AvoidFirstRun() {
const [count, setCount] = useState(0);
const isFirst = useRef(true);
useEffect(() => {
if (isFirst.current) {
isFirst.current = false;
return; // 跳过第一次
}
console.log("count 变化执行:", count);
}, [count]);
return (
<View>
<Text>{count}</Text>
<Button title="增加" onPress={() => setCount(count + 1)} />
</View>
);
}
这里 isFirst.current
在第一次渲染时为 true
,这样就能跳过初始执行。
useEffect 的注意事项
- 依赖项数组写错:如果依赖项漏写,可能导致逻辑不更新。如果依赖项写了函数引用,每次渲染都会触发,可以用
useCallback
优化。 - 忘记清理副作用:比如监听事件、定时器,如果不清理,会导致性能问题甚至内存泄漏。
- 多余请求: 如果依赖项是一个对象或数组,每次都会生成新引用,导致
useEffect
频繁触发。可以用useMemo
进行优化。
总结
useEffect
是 React Native 中处理副作用的核心工具。根据依赖项数组的不同,useEffect
可以实现以下功能:
- 每次渲染执行
- 只在第一次执行
- 依赖项变化时执行
常见场景包括以下几项:
- 请求数据;
- 设置/清理定时器;
- 控制逻辑避免首次执行。