
摘要:本文旨在帮助开发者理解并解决 React 应用中 useEffect 钩子意外运行两次的问题。我们将深入探讨导致此现象的常见原因,并提供相应的解决方案,确保你的副作用函数仅在预期时机执行,避免潜在的性能问题和数据不一致。通过本文的学习,你将能够更好地控制 useEffect 的行为,构建更稳定、更高效的 React 应用。
在 React 开发中,useEffect 是一个强大的钩子,用于处理副作用操作,如数据获取、订阅事件等。然而,有时开发者会遇到 useEffect 运行两次的意外情况,这可能会导致不必要的 API 调用、资源浪费,甚至数据错误。本文将深入探讨导致 useEffect 运行两次的常见原因,并提供相应的解决方案。
React Strict Mode
React 的 Strict Mode 是一种开发模式,旨在帮助开发者发现潜在的问题。在 Strict Mode 下,React 会故意调用某些函数两次,包括 useEffect 中的副作用函数。这有助于开发者识别那些不纯的副作用函数,即那些会修改组件外部状态的函数。
解决方案:
示例:
假设你的 index.tsx 文件中使用了 Strict Mode:
import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);你可以暂时移除 <React.StrictMode> 来禁用 Strict Mode。
依赖项问题
useEffect 的第二个参数是一个依赖项数组,用于指定 effect 应该在哪些值发生变化时重新运行。如果依赖项数组为空 [],则 effect 只会在组件首次渲染时运行一次。但是,如果依赖项数组中的值发生变化,effect 就会重新运行。
解决方案:
示例:
import React, { useState, useEffect } from 'react';
function MyComponent() {
const [count, setCount] = useState(0);
const [name, setName] = useState('Initial Name');
useEffect(() => {
console.log('Effect is running');
document.title = `Count: ${count}`;
}, [count]); // Only runs when 'count' changes
return (
<div>
<p>Count: {count}</p>
<button onClick={() => setCount(count + 1)}>Increment</button>
<input
type="text"
value={name}
onChange={(e) => setName(e.target.value)}
/>
</div>
);
}
export default MyComponent;在这个例子中,useEffect 只会在 count 的值发生变化时运行,因为 count 是唯一的依赖项。如果添加了 name 作为依赖项,那么每次 name 改变时,useEffect 也会运行。
组件重新渲染
如果组件由于其他原因重新渲染,useEffect 也会重新运行。这可能是由于父组件的状态更新、props 改变,或者使用了 forceUpdate 等方法。
解决方案:
示例:
import React, { useState, useEffect, memo } from 'react';
const MyChildComponent = memo(({ data }) => {
useEffect(() => {
console.log('Child Effect running with data:', data);
}, [data]);
return <p>Data from parent: {data}</p>;
});
function MyParentComponent() {
const [parentCount, setParentCount] = useState(0);
return (
<div>
<button onClick={() => setParentCount(parentCount + 1)}>
Increment Parent Count
</button>
<MyChildComponent data={parentCount} />
</div>
);
}
export default MyParentComponent;在这个例子中,MyChildComponent 使用 memo 进行包裹,只有当 data prop 发生变化时才会重新渲染,从而避免了不必要的 useEffect 运行。
Next.js 开发环境下的 Fast Refresh
在使用 Next.js 开发时,Fast Refresh 功能可能会导致 useEffect 在保存代码时运行两次。这是为了提供更快的开发体验,但有时会导致副作用函数意外执行。
解决方案:
示例(基于问题中的代码):
问题中的代码存在 loading 状态管理的问题,导致 useEffect 运行两次,并且 loading 状态始终为 false。以下是修改后的代码:
import { type AppType } from 'next/app';
import { api } from '~/utils/api';
import '~/styles/globals.css';
import Nav from '~/components/Nav';
import { useEffect, useState } from 'react';
const MyApp: AppType = ({ Component, pageProps }) => {
const [showCart, setShowCart] = useState(false);
const [loading, setLoading] = useState(true); // Set initial loading state to true
const createSession = api.user.createSession.useMutation();
const generateId = async (): Promise<string | undefined> => {
const res = await createSession.mutateAsync();
if (res.response) {
return res.response.id;
} else if (res.error) {
return res.error;
}
};
const setSessionId = async () => {
const tmp: string | undefined = await generateId();
if (tmp) document.cookie = `sessionId=${tmp}`;
setLoading(false);
};
useEffect(() => {
const getSessionId = () => {
const cookieString: string = document.cookie;
const cookies: string[] = cookieString.split(';') || [];
let sessionId: string | null = null;
for (let i = 0; i < cookies.length; i++) {
const cookie: string | undefined = cookies[i];
if (!cookie || cookie.trim() === '') {
continue;
}
if (cookie.trim().startsWith('sessionId=')) {
sessionId = cookie.trim().substring('sessionId='.length);
break;
}
}
return sessionId;
};
if (!getSessionId()) {
setSessionId();
} else {
setLoading(false); // Set loading to false if session ID is present
}
}, []);
return (
<>
<Nav showCart={showCart} setShowCart={setShowCart} />
{loading && <h1>LOADING</h1>} {/* Show LOADING only if loading is true */}
<Component {...pageProps} />
</>
);
};
export default api.withTRPC(MyApp);关键修改:
useEffect 运行两次通常是由于 React Strict Mode、依赖项问题、组件重新渲染或 Next.js Fast Refresh 引起的。通过理解这些原因,并采取相应的解决方案,你可以更好地控制 useEffect 的行为,构建更稳定、更高效的 React 应用。在调试 useEffect 时,始终要仔细检查依赖项、组件渲染逻辑,并考虑是否启用了 Strict Mode 或 Fast Refresh。
以上就是解决 React 中 useEffect 运行两次的问题的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号