文章目录
-
- 问题
- 分析
- 解决
-
- 方式一:使用状态标志
- [方式二:使用 useRef](#方式二:使用 useRef)
- 方式三:移除严格模式
- [方式四:使用 useCallback](#方式四:使用 useCallback)
问题
这是一个 React Demo 项目 ,启动后在 Chrome 控制台里发现 控制台 被打印了 "两次",但是,在生产环境中,这个问题通常不会出现,所以可以主要关注开发环境中的行为。
javascript
// 入口文件
import { StrictMode } from 'react';
import * as ReactDOMClient from 'react-dom/client';
import App from './App';
const root = ReactDOMClient.createRoot(document.getElementById('root'));
root.render(
<StrictMode>
<App />
</StrictMode>
);
javascript
// 组件代码
import React, { useEffect } from 'react';
const App = () => {
useEffect(() => {
console.log('组件挂载完成!');
}, []);
return <>Hello world!</>;
};
分析
-
这是 React18 才新增的特性。
-
仅在开发模式("development")下,且使用了严格模式("Strict Mode")下会触发。
生产环境("production")模式下和原来一样,仅执行一次。
-
之所以执行两次,是为了模拟立即卸载组件和重新挂载组件。
为了帮助开发者提前发现重复挂载造成的 Bug 的代码。
同时,也是为了以后 React的新功能做铺垫。
未来会给 React 增加一个特性,允许 React 在保留状态的同时,能够做到仅仅对UI部分的添加和删除。
让开发者能够提前习惯和适应,做到组件的卸载和重新挂载之后, 重复执行 useEffect的时候不会影响应用正常运行。
-
每次组件渲染时,React 都会更新页面 UI,然后运行 useEffect 中的代码
-
Effect 在屏幕更新之后的 rendering 进程结束的时候执行。
解决
方式一:使用状态标志
javascript
const [isDataFetched, setIsDataFetched] = useState(false);
useEffect(() => {
if (!isDataFetched) {
getDataList();
setIsDataFetched(true);
}
}, [isDataFetched]);
方式二:使用 useRef
javascript
const dataFetchedRef = useRef(false);
useEffect(() => {
if (dataFetchedRef.current) return;
dataFetchedRef.current = true;
getDataList();
}, []);
方式三:移除严格模式
方式四:使用 useCallback
将 getDataList 函数用 useCallback 包裹,可以确保它的引用稳定性。