深入理解 React 組件的生命周期:從創建到銷毀的全過程

React 作為當今最流行的前端框架之一,其組件生命周期是每個 React 開發者必須掌握的核心概念。本文將全面剖析 React 組件的生命周期,包括類組件的各個生命周期方法和函數組件如何使用 Hooks 模擬生命周期行為,幫助開發者編寫更高效、更健壯的 React 應用。

一、React 組件生命周期概述

React 組件的生命周期指的是一個組件從創建、更新到銷毀的整個過程。在這個過程中,React 提供了許多"生命周期方法",允許開發者在特定的階段執行自定義代碼。理解這些生命周期方法對于控制組件行為、優化性能以及管理副作用至關重要。

React 的生命周期可以分為三個主要階段:

  1. 掛載階段(Mounting):組件被創建并插入到 DOM 中

  2. 更新階段(Updating):組件的 props 或 state 發生變化時的重新渲染過程

  3. 卸載階段(Unmounting):組件從 DOM 中移除

此外,React 16 還引入了錯誤處理生命周期方法,用于捕獲和處理組件樹中的 JavaScript 錯誤。

二、類組件的生命周期詳解

1. 掛載階段(Mounting)

掛載階段是組件第一次被創建并插入到 DOM 中的過程,包含以下生命周期方法:

constructor()
constructor(props) {super(props);this.state = { count: 0 };this.handleClick = this.handleClick.bind(this);
}
  • 最先執行的生命周期方法

  • 必須調用?super(props),否則?this.props?將會是 undefined

  • 唯一可以直接修改?this.state?的地方

  • 用于初始化 state 和綁定事件處理方法

static getDerivedStateFromProps()
static getDerivedStateFromProps(props, state) {if (props.value !== state.prevValue) {return {value: props.value,prevValue: props.value};}return null;
}
  • 在 render 方法之前調用,無論是初始掛載還是后續更新

  • 應返回一個對象來更新 state,或返回 null 不更新

  • 用于 state 依賴于 props 變化的罕見情況

  • 此方法無權訪問組件實例(即不能使用 this)

render()
render() {return <div>{this.state.count}</div>;
}
  • 類組件中唯一必須實現的方法

  • 應該是一個純函數,不修改組件狀態,不與瀏覽器交互

  • 返回以下類型之一:

    • React 元素(JSX)

    • 數組或 fragments

    • Portals

    • 字符串或數值(渲染為文本節點)

    • 布爾值或 null(不渲染任何內容)

componentDidMount()
componentDidMount() {// 典型用法:fetchData().then(data => this.setState({ data }));// 或this.subscription = dataSource.subscribe(this.handleDataChange);
}
  • 組件掛載(插入 DOM 樹)后立即調用

  • 適合執行有副作用的操作:

    • 網絡請求

    • 設置訂閱

    • 手動操作 DOM

  • 可以在此處直接調用 setState(),但會觸發額外渲染

2. 更新階段(Updating)

當組件的 props 或 state 發生變化時,會觸發更新階段的生命周期方法:

static getDerivedStateFromProps()
  • 同掛載階段,在每次渲染前調用

shouldComponentUpdate()
shouldComponentUpdate(nextProps, nextState) {// 只有當count變化時才重新渲染return nextState.count !== this.state.count;
}
  • 決定組件是否應該更新

  • 返回 false 可以阻止組件重新渲染

  • 主要用于性能優化

  • 不建議深層比較或使用 JSON.stringify(),影響性能

  • 考慮使用 PureComponent 替代手動實現

render()
  • 同掛載階段

getSnapshotBeforeUpdate()
getSnapshotBeforeUpdate(prevProps, prevState) {if (prevProps.items.length < this.props.items.length) {const list = this.listRef.current;return list.scrollHeight - list.scrollTop;}return null;
}
  • 在最近一次渲染輸出(提交到 DOM 節點)之前調用

  • 使得組件能在 DOM 變化前捕獲一些信息(如滾動位置)

  • 返回值將作為 componentDidUpdate() 的第三個參數

componentDidUpdate()
componentDidUpdate(prevProps, prevState, snapshot) {if (this.props.userID !== prevProps.userID) {this.fetchData(this.props.userID);}if (snapshot !== null) {const list = this.listRef.current;list.scrollTop = list.scrollHeight - snapshot;}
}
  • 更新完成后調用(首次渲染不會執行)

  • 適合執行有副作用的操作:

    • 網絡請求(需比較 props)

    • DOM 操作

  • 可以調用 setState(),但必須包裹在條件語句中,否則會導致無限循環

3. 卸載階段(Unmounting)

componentWillUnmount()
componentWillUnmount() {clearInterval(this.timerID);this.subscription.unsubscribe();
}
  • 組件卸載及銷毀前調用

  • 用于執行必要的清理操作:

    • 清除定時器

    • 取消網絡請求

    • 清理訂閱

  • 不應調用 setState(),因為組件永遠不會重新渲染

4. 錯誤處理

React 16 引入了錯誤邊界的概念,用于捕獲子組件樹中的 JavaScript 錯誤。

static getDerivedStateFromError()
static getDerivedStateFromError(error) {return { hasError: true };
}
  • 在后代組件拋出錯誤后被調用

  • 接收錯誤作為參數

  • 應返回一個狀態對象以更新 state,用于渲染備用 UI

componentDidCatch()
componentDidCatch(error, info) {logErrorToService(error, info.componentStack);
}
  • 在后代組件拋出錯誤后被調用

  • 接收兩個參數:

    • error - 拋出的錯誤

    • info - 包含 componentStack 鍵的對象

  • 用于記錄錯誤信息

三、函數組件的"生命周期"

隨著 React Hooks 的引入,函數組件現在也能實現類組件的生命周期功能。以下是常用 Hooks 與生命周期方法的對應關系:

useState - 狀態管理

const [count, setCount] = useState(0);
  • 相當于類組件中的 this.state 和 this.setState

  • 可以在函數組件中添加局部 state

useEffect - 副作用管理

useEffect(() => {// 相當于 componentDidMount 和 componentDidUpdatedocument.title = `You clicked ${count} times`;return () => {// 相當于 componentWillUnmount// 清理函數};
}, [count]); // 僅在 count 更改時更新
  • 組合了 componentDidMount, componentDidUpdate 和 componentWillUnmount

  • 第一個參數是 effect 函數,第二個參數是依賴數組

  • 返回的函數是清理函數,在組件卸載時執行

useLayoutEffect

useLayoutEffect(() => {// 在 DOM 更新后同步執行const { width } = node.getBoundingClientRect();setWidth(width);
});
  • 與 useEffect 簽名相同,但調用時機不同

  • 在 DOM 變更后同步觸發

  • 適用于需要讀取 DOM 布局并同步重新渲染的情況

useMemo 和 useCallback - 性能優化

const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
const memoizedCallback = useCallback(() => doSomething(a, b), [a, b]);
  • 相當于 shouldComponentUpdate 的優化

  • 用于避免不必要的計算和渲染

四、新舊生命周期對比與最佳實踐

React 16.3 對生命周期方法進行了重大調整,廢棄了一些不安全的生命周期方法:

廢棄的方法:

  • componentWillMount

  • componentWillReceiveProps

  • componentWillUpdate

這些方法被標記為不安全主要是因為:

  1. 它們經常被誤用和濫用

  2. 在異步渲染中可能導致問題

  3. 容易引入副作用和錯誤

最佳實踐建議:

  1. 將數據獲取移到 componentDidMount 或 useEffect 中

  2. 使用 getDerivedStateFromProps 替代 componentWillReceiveProps

  3. 使用 getSnapshotBeforeUpdate 替代 componentWillUpdate

  4. 考慮使用函數組件和 Hooks 替代類組件

  5. 謹慎使用派生 state,多數情況下可以通過提升 state 或受控組件解決

五、實際應用場景示例

場景1:數據獲取

class UserProfile extends React.Component {state = { user: null, loading: true };async componentDidMount() {const user = await fetchUser(this.props.userId);this.setState({ user, loading: false });}async componentDidUpdate(prevProps) {if (this.props.userId !== prevProps.userId) {this.setState({ loading: true });const user = await fetchUser(this.props.userId);this.setState({ user, loading: false });}}componentWillUnmount() {// 取消可能的未完成請求}render() {// 渲染用戶信息}
}

場景2:滾動位置恢復

class ScrollList extends React.Component {getSnapshotBeforeUpdate(prevProps) {if (prevProps.items.length < this.props.items.length) {const list = this.listRef.current;return list.scrollHeight - list.scrollTop;}return null;}componentDidUpdate(prevProps, prevState, snapshot) {if (snapshot !== null) {const list = this.listRef.current;list.scrollTop = list.scrollHeight - snapshot;}}render() {return (<div ref={this.listRef}>{/* 列表內容 */}</div>);}
}

場景3:錯誤邊界

class ErrorBoundary extends React.Component {state = { hasError: false };static getDerivedStateFromError(error) {return { hasError: true };}componentDidCatch(error, info) {logErrorToService(error, info.componentStack);}render() {if (this.state.hasError) {return <h1>Something went wrong.</h1>;}return this.props.children; }
}

總結

React 組件的生命周期是 React 應用的核心機制,理解這些生命周期方法對于構建高效、可靠的 React 應用至關重要。隨著 React 的發展,生命周期方法也在不斷演進,從類組件的各種生命周期方法到函數組件的 Hooks,React 提供了更靈活、更簡潔的方式來管理組件的生命周期。

對于新項目,建議優先考慮使用函數組件和 Hooks,它們提供了更簡潔的代碼組織和更強大的組合能力。對于現有項目,了解類組件的生命周期仍然很重要,特別是在維護老代碼時。

記住,生命周期方法是你控制組件行為的工具,正確使用它們可以:

  • 優化性能

  • 管理副作用

  • 處理錯誤

  • 保持代碼整潔和可維護

通過掌握 React 組件的生命周期,你將能夠構建更強大、更可靠的 React 應用程序。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/77673.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/77673.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/77673.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

緩存 --- Redis性能瓶頸和大Key問題

緩存 --- Redis性能瓶頸和大Key問題 內存瓶頸網絡瓶頸CPU 瓶頸持久化瓶頸大key問題優化方案 Redis 是一個高性能的內存數據庫&#xff0c;但在實際使用中&#xff0c;可能會在內存、網絡、CPU、持久化、大鍵值對等方面遇到性能瓶頸。下面從這些方面詳細分析 Redis 的性能瓶頸&a…

Python爬蟲與代理IP:高效抓取數據的實戰指南

目錄 一、基礎概念解析 1.1 爬蟲的工作原理 1.2 代理IP的作用 二、環境搭建與工具選擇 2.1 Python庫準備 2.2 代理IP選擇技巧 三、實戰步驟分解 3.1 基礎版&#xff1a;單線程免費代理 3.2 進階版&#xff1a;多線程付費代理池 3.3 終極版&#xff1a;Scrapy框架自動…

Nginx HTTP 414 與“大面積”式洪水攻擊聯合防御實戰

一、引言 在大規模分布式應用中&#xff0c;Nginx 常作為前端負載均衡和反向代理服務器。攻擊者若結合超長 URI/頭部攻擊&#xff08;觸發 HTTP 414&#xff09;與海量洪水攻擊&#xff0c;可在網絡層與應用層形成雙重打擊&#xff1a;一方面耗盡緩沖區和內存&#xff0c;另一…

【上位機——MFC】運行時類信息機制

運行時類信息機制的使用 類必須派生自CObject類內必須添加聲明宏DECLARE_DYNAMIC(theClass)3.類外必須添加實現宏 IMPLEMENT_DYNAMIC(theClass,baseClass) 具備上述三個條件后&#xff0c;CObject::IsKindOf函數就可以正確判斷對象是否屬于某個類。 代碼示例 #include <…

Maven插件管理的基本原理

&#x1f9d1; 博主簡介&#xff1a;CSDN博客專家&#xff0c;歷代文學網&#xff08;PC端可以訪問&#xff1a;https://literature.sinhy.com/#/?__c1000&#xff0c;移動端可微信小程序搜索“歷代文學”&#xff09;總架構師&#xff0c;15年工作經驗&#xff0c;精通Java編…

卷積神經網絡--手寫數字識別

本文我們通過搭建卷積神經網絡模型&#xff0c;實現手寫數字識別。 pytorch中提供了手寫數字的數據集 &#xff0c;我們可以直接從pytorch中下載 MNIST中包含70000張手寫數字圖像&#xff1a;60000張用于訓練&#xff0c;10000張用于測試 圖像是灰度的&#xff0c;28x28像素 …

大文件分片上傳進階版(新增md5校驗、上傳進度展示、并行控制,智能分片、加密上傳、斷點續傳、自動重試),實現四位一體的網絡感知型大文件傳輸系統?

上篇文章我們總結了大文件分片上傳的主要核心&#xff0c;但是我對md5校驗和上傳進度展示這塊也比較感興趣&#xff0c;所以在deepseek的幫助下&#xff0c;擴展了一下我們的代碼&#xff0c;如果有任何問題和想法&#xff0c;非常歡迎大家在評論區與我交流&#xff0c;我需要學…

C# 點擊導入,將需要的參數傳遞到彈窗的頁面

點擊導入按鈕&#xff0c;獲取本頁面的datagridview標題的結構&#xff0c;并傳遞到導入界面。 新增一個datatable用于存儲datagridview的caption和name&#xff0c;這里用的是devexpress組件中的gridview。 DataTable dt new DataTable(); DataColumn CAPTION …

android的 framework 是什么

Android的Framework&#xff08;框架&#xff09;是Android系統的核心組成部分&#xff0c;它為開發者提供了一系列的API&#xff08;應用程序編程接口&#xff09;&#xff0c;使得開發者能夠方便地創建各種Android應用。以下是關于它的詳細介紹&#xff1a; 位置與架構 在A…

【MySQL】表的約束(主鍵、唯一鍵、外鍵等約束類型詳解)、表的設計

目錄 1.數據庫約束 1.1 約束類型 1.2 null約束 — not null 1.3 unique — 唯一約束 1.4 default — 設置默認值 1.5 primary key — 主鍵約束 自增主鍵 自增主鍵的局限性&#xff1a;經典面試問題&#xff08;進階問題&#xff09; 1.6 foreign key — 外鍵約束 1.7…

數據結構-C語言版本(三)棧

數據結構中的棧&#xff1a;概念、操作與實戰 第一部分 棧分類及常見形式 棧是一種遵循后進先出(LIFO, Last In First Out)原則的線性數據結構。棧主要有以下幾種實現形式&#xff1a; 1. 數組實現的棧&#xff08;順序棧&#xff09; #define MAX_SIZE 100typedef struct …

如何以特殊工藝攻克超薄電路板制造難題?

一、超薄PCB的行業定義與核心挑戰 超薄PCB通常指厚度低于1.0毫米的電路板&#xff0c;而高端產品可進一步壓縮至0.4毫米甚至0.2毫米以下。這類電路板因體積小、重量輕、熱傳導性能優異&#xff0c;被廣泛應用于折疊屏手機、智能穿戴設備、醫療植入器械及新能源汽車等領域。然而…

AI 賦能 3D 創作!Tripo3D 全功能深度解析與實操教程

大家好&#xff0c;歡迎來到本期科技工具分享&#xff01; 今天要給大家帶來一款革命性的 AI 3D 模型生成平臺 ——Tripo3D。 無論你是游戲開發者、設計師&#xff0c;還是 3D 建模愛好者&#xff0c;只要想降低創作門檻、提升效率&#xff0c;這款工具都值得深入了解。 接下…

如何理解抽象且不易理解的華為云 API?

API的概念在華為云的使用中非常抽象&#xff0c;且不容易理解&#xff0c;用通俗的語言 形象的比喻來講清楚——什么是華為云 API&#xff0c;怎么用&#xff0c;背后原理&#xff0c;以及主要元素有哪些&#xff0c;盡量讓新手也能明白。 &#x1f9e0; 一句話先理解&#xf…

第 7 篇:總結與展望 - 時間序列學習的下一步

第 7 篇&#xff1a;總結與展望 - 時間序列學習的下一步 (圖片來源: Guillaume Hankenne on Pexels) 恭喜你&#xff01;如果你一路跟隨這個系列走到了這里&#xff0c;那么你已經成功地完成了時間序列分析的入門之旅。我們從零開始&#xff0c;一起探索了時間數據的基本概念、…

PPT無法編輯怎么辦?原因及解決方法全解析

在日常辦公中&#xff0c;我們經常會遇到需要編輯PPT的情況。然而&#xff0c;有時我們會發現PPT文件無法編輯&#xff0c;這可能由多種原因引起。今天我們來看看PPT無法編輯的幾種常見原因&#xff0c;并提供實用的解決方法&#xff0c;幫助你輕松應對。 原因1&#xff1a;文…

前端面試題---GET跟POST的區別(Ajax)

GET 和 POST 是兩種 HTTP 請求方式&#xff0c;它們在傳輸數據的方式和所需空間上有一些重要區別&#xff1a; ? 一句話概括&#xff1a; GET 數據放在 URL 中&#xff0c;受限較多&#xff1b;POST 數據放在請求體中&#xff0c;空間更大更安全。 &#x1f4e6; 1. 所需空間…

第 5 篇:初試牛刀 - 簡單的預測方法

第 5 篇&#xff1a;初試牛刀 - 簡單的預測方法 經過前面四篇的學習&#xff0c;我們已經具備了處理時間序列數據的基本功&#xff1a;加載、可視化、分解以及處理平穩性。現在&#xff0c;激動人心的時刻到來了——我們要開始嘗試預測 (Forecasting) 未來&#xff01; 預測是…

從代碼學習深度學習 - 學習率調度器 PyTorch 版

文章目錄 前言一、理論背景二、代碼解析2.1. 基本問題和環境設置2.2. 訓練函數2.3. 無學習率調度器實驗2.4. SquareRootScheduler 實驗2.5. FactorScheduler 實驗2.6. MultiFactorScheduler 實驗2.7. CosineScheduler 實驗2.8. 帶預熱的 CosineScheduler 實驗三、結果對比與分析…

k8s 基礎入門篇之開啟 firewalld

前面在部署k8s時&#xff0c;都是直接關閉的防火墻。由于生產環境需要開啟防火墻&#xff0c;只能放行一些特定的端口&#xff0c; 簡單記錄一下過程。 1. firewall 與 iptables 的關系 1.1 防火墻&#xff08;Firewall&#xff09; 定義&#xff1a; 防火墻是網絡安全系統&…