要更详细地理解React中的useEffect
Hook,我们可以通过查看一些核心代码来深入分析。以下是useEffect
在React源码中的一个简化版本,它展示了useEffect
是如何工作的:
// React内部的Hook结构
const Hook = {
memoizedState: null, // 保存Hook的状态
baseState: null, // 初始状态
baseUpdate: null, // 初始更新
queue: null, // 更新队列
next: null // 链接到下一个Hook
};
// 调度更新的函数
function scheduleUpdateOnFiber(fiber, lane, eventTime) {
// ...调度更新逻辑
}
// useEffect的实现
function useEffect(create, deps) {
const fiber = currentlyRenderingFiber; // 当前渲染的Fiber节点
const hook = mountWorkInProgressHook(); // 挂载Hook
// 依赖项检查
const nextDeps = deps === undefined ? null : deps;
let destroy = undefined;
if (currentHook) {
// 获取上一次的依赖项
const prevDeps = currentHook.memoizedState[1];
if (areHookInputsEqual(nextDeps, prevDeps)) {
// 依赖项没有变化,跳过effect
return;
}
// 获取上一次的销毁函数
destroy = currentHook.memoizedState[0];
}
// 将副作用推入副作用链表
hook.memoizedState = pushEffect(
HookHasEffect | HookPassive, // 标志位,表示这是一个有副作用的Passive effect
create, // 创建副作用的函数
destroy, // 清理副作用的函数
nextDeps // 依赖项数组
);
// 调度更新
scheduleUpdateOnFiber(fiber, UpdateLanes, eventTime);
}
// 检查输入是否相等的函数
function areHookInputsEqual(nextDeps, prevDeps) {
// ...比较逻辑
}
// 将副作用推入链表的函数
function pushEffect(tag, create, destroy, deps) {
// ...推入逻辑
}
在这个简化的源码中,我们可以看到useEffect
是如何利用React的Fiber架构来调度和执行副作用的。useEffect
会在组件渲染后将副作用函数和清理函数推入一个链表中,React会在适当的时机执行这些副作用。
在React中,useEffect
Hook允许你在函数组件中执行副作用操作。
- 条件执行:只在特定变量改变时执行
useEffect
。
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
// 仅在count变化时执行
useEffect(() => {
document.title = `You clicked ${count} times`;
}, [count]); // 依赖项数组
return (
<div>
<p>You clicked {count} times</p>
<button onClick={() => setCount(count + 1)}>
Click me
</button>
</div>
);
}
- 清理函数:组件卸载前执行清理操作。
import React, { useState, useEffect } from 'react';
function MyComponent() {
useEffect(() => {
// 设置事件监听
window.addEventListener('resize', handleResize);
// 清理函数
return () => {
window.removeEventListener('resize', handleResize);
};
}, []); // 空依赖项数组表示仅在挂载和卸载时执行
function handleResize() {
// ...
}
// ...
}
- 跳过首次执行:使用
useRef
来跳过组件挂载时的副作用执行。
import React, { useState, useEffect, useRef } from 'react';
function MyComponent() {
const firstUpdate = useRef(true);
useEffect(() => {
if (firstUpdate.current) {
firstUpdate.current = false;
return;
}
// 你的副作用逻辑
});
// ...
}
- 自定义Hook:封装复杂逻辑。
import React, { useState, useEffect } from 'react';
function useCustomHook() {
const [data, setData] = useState(null);
useEffect(() => {
fetchData().then(setData);
}, []);
return data;
}
function MyComponent() {
const data = useCustomHook();
// 使用data渲染组件
}
- 与
useContext
或useReducer
结合:useEffect
可以与其他Hook结合使用。
import React, { useContext, useEffect } from 'react';
import MyContext from './MyContext';
function MyComponent() {
const value = useContext(MyContext);
useEffect(() => {
// 使用上下文值的副作用逻辑
}, [value]);
// ...
}
- 优化性能:使用
useMemo
和useCallback
减少不必要的渲染。
import React, { useState, useEffect, useCallback } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const increment = useCallback(() => {
setCount(c => c + 1);
}, []);
useEffect(() => {
// 使用increment的副作用逻辑
}, [increment]);
// ...
}
以上就是一些useEffect
的认识。希望通过这篇文章,你可以更好地理解如何在React应用中有效地使用useEffect
Hook。