https://juejin.im/post/5e64d3eff265da57671bd080html
前言
StrictMode
是 React
在 16.3
版本新增的一個組件,按照官方的說法:react
StrictMode 是一個用來突出顯示應用程序中潛在問題的工具。與 Fragment 同樣,StrictMode 不會渲染任何可見的 UI。它爲其後代元素觸發額外的檢查和警告。git
我相信不少人和我同樣都知道 StrictMode
相似 JavaScript
中的 use strict
, 可讓 React
程序在更嚴格的條件下運行,也已經在項目中使用了它,可是仍是不太瞭解 StrictMode
怎樣檢測出程序的問題。下面就經過一個問題拋磚引玉,給你們簡單的介紹一下 StrictMode
。github
問題

App
組件裏
state.id
的值會是多少?
我相信你們對 React
有了解的話就會不假思索地說出 state.id = 1
這個答案,這個答案在生產環境中確實是正確答案,畢竟在 React
的官方文檔中告訴了咱們 Class Component
的 constructor
只會在渲染時執行一次。可是你在開發環境中運行的時候,顯示的答案並不如咱們所想,來一塊兒康康 運行的結果 吧: 數組

constructor
只會執行一次嗎?難道
React
騙了咱們?

這個問題出現的「罪魁禍首」就是 React.StrictMode
,它在開發環境中將 constructor
函數調用了兩次,至於爲何調用兩次?其實爲了檢測意外的反作用,經過調用兩次的方式將一些隱藏地比較深的反作用放大,讓開發者更好的發現它。詳情的內容能夠查看下面的介紹。瀏覽器
StrictMode 功能介紹
一、檢測意外的反作用
從概念上講,React 分兩個階段工做:安全
渲染 階段會肯定須要進行哪些更改,好比 DOM
。在此階段,React
調用 render
,而後將結果與上次渲染的結果進行比較。 提交 階段發生在當 React
應用變化時。(對於 React DOM
來講,會發生在 React
插入,更新及刪除 DOM
節點的時候。)在此階段,React
還會調用 componentDidMount
和 componentDidUpdate
之類的生命週期方法。 提交階段一般會很快,但渲染過程可能很慢。所以,即將推出的 concurrent
模式 (默認狀況下未啓用) 將渲染工做分解爲多個部分,對任務進行暫停和恢復操做以免阻塞瀏覽器。這意味着 React
能夠在提交以前屢次調用渲染階段生命週期的方法,或者在不提交的狀況下調用它們(因爲出現錯誤或更高優先級的任務使其中斷)。bash
渲染階段的生命週期包括如下 class 組件方法:async
- constructor - componentWillMount (or UNSAFE_componentWillMount) - componentWillReceiveProps (or UNSAFE_componentWillReceiveProps) - componentWillUpdate (or UNSAFE_componentWillUpdate) - getDerivedStateFromProps - shouldComponentUpdate - render - setState 更新函數(第一個參數) 複製代碼
由於上述方法可能會被屢次調用,因此不要在它們內部編寫反作用相關的代碼,這點很是重要。忽略此規則可能會致使各類問題的產生,包括內存泄漏和或出現無效的應用程序狀態。不幸的是,這些問題很難被發現,由於它們一般具備非肯定性。函數
嚴格模式不能自動檢測到你的反作用,但它能夠幫助你發現它們,使它們更具肯定性。經過在開發環境下故意重複調用如下方法來實現的該操做:
- class 組件的 constructor 方法 - render 方法 - setState 更新函數 (第一個參數) - 靜態的 getDerivedStateFromProps 生命週期方法 複製代碼
以上這段話全都來自於 React
的官方中文文檔,由於文檔上已經對於 StrictMode
是爲何要檢測意外的反作用以及怎麼檢測意外的反作用介紹得實在太詳細了,筆者以爲沒啥好補充的了,就直接搬運了過來😂。
以 render
方法爲例,簡單看下 StrictMode
關於檢測意外的反作用的實現:

真的就像文檔介紹的那麼簡單,僅僅只是對方法調用了兩次,沒有任何的比較之類的動做。
二、識別不安全的生命週期
React
在 16.3
版本中將一些生命週期方法列爲了避免安全的生命週期。至於爲何這些生命週期方法是不安全的,能夠參考這篇博客的開頭,主要仍是考慮到了使用這些生命週期的代碼在 React
的將來版本中更有可能出現 bug。
componentWillMount
componentWillReceiveProps
componentWillUpdate
StrictMode
就能夠幫助咱們檢測代碼中是否有使用到這些不安全的生命週期方法。在 Class Component 實例化完成後,會去組件實例上尋找有沒有 componentWillMount
或 UNSAFE_componentWillMount
這些不安全的生命週期的方法,有就 push 到一個數組裏,最後統一在控制檯發出警告️。代碼實現比較簡單,感興趣能夠看下ReactStrictModeWarnings.js 中的 recordUnsafeLifecycleWarnings
和 flushPendingUnsafeLifecycleWarnings
這兩個方法。

三、對於使用廢棄的 findDOMNode 方法的警告
React
支持用 findDOMNode
來在給定 class
實例的狀況下在樹中搜索 DOM
節點。一般你不須要這樣作,由於你能夠將 ref
直接綁定到 DOM
節點。
findDOMNode
也可用於 class
組件,但它違反了抽象原則,它使得父組件須要單獨渲染子組件。它會產生重構危險,你不能更改組件的實現細節,由於父組件可能正在訪問它的 DOM
節點。findDOMNode
只返回第一個子節點,可是使用 Fragments
,組件能夠渲染多個 DOM
節點。findDOMNode
是一個只讀一次的 API。調用該方法只會返回第一次查詢的結果。若是子組件渲染了不一樣的節點,則沒法跟蹤此更改。所以,findDOMNode
僅在組件返回單個且不可變的 DOM
節點時纔有效。
這段話也來自官方文檔,由於findDOMNode
的這些問題,因此React
決定在 StrictMode
中廢棄它,在調用 findDOMNode
會去判斷是否在 StrictMode 模式下,有則在控制檯打印出警告。

四、檢測過期的 context API
由於舊的 Context API
在 context 的值有更新時,沒辦法保證全部子節點必定能更新(由於中間父組件的 shouldComponentUpdate
返回 false
,那麼使用到該值的後代組件不會進行更新)的問題,因此 React
在 16.3 版本提出了新的 Context API
,因此在 StrictMode
中會檢測應用中是否使用到了過期的 Context API
。
因爲老的 Context API 會在 Context 提供者上綁定 childContextTypes
和 getChildContext
以及在 在 Context 的使用者上綁定用來訪問 context 的 contextTypes
屬性,因此 StrictMode
只要在組件實例化完成後判斷實例上有沒有這幾個屬性就能判斷是否使用了老的 Context API,而後做出統一的警告。


五、對於使用字符串 ref API 的警告
這部份內容雖然官方文檔上有提到,可是通過筆者的實驗,並不能在 StrictMode
下對使用了 string ref API
的行爲在控制檯產生警告,因此就不在這裏多作說起。
寫在最後
StrictMode
確實能夠幫助咱們讓 React
程序運行地更好,更健壯,這個毋庸置疑。可是筆者認爲在檢測意外的反作用這一點上 React 作的對開發者不夠友好吧,雖然對於一部分方法調用兩次能夠更容易發現出意外的反作用,可是對於剛接觸 React
的人或者對 StrictMode
瞭解不夠的人來講,在開發的時候更可能會認爲是 React
出了問題或者本身的寫法有問題,致使了重複調用,浪費沒必要要的 debug 時間。這一方面可能須要作出更友好的提示。