react教程要點記錄

描述UI

  • React 組件是一段可以 使用標簽進行擴展 的 JavaScript 函數(shù)。
  • React 組件是常規(guī)的 JavaScript 函數(shù),但 組件的名稱必須以大寫字母開頭
  • 同一文件中,有且僅有一個默認導出,但可以有多個具名導出!
  • 為什么需要一個根標簽
    • JSX 雖然看起來很像 HTML,但在底層其實被轉化為了 JavaScript 對象,你不能在一個函數(shù)中返回多個對象,除非用一個數(shù)組把他們包裝起來。這就是為什么多個 JSX 標簽必須要用一個父元素或者 Fragment 來包裹。
  • 切勿將數(shù)字放在 && 左側.左側為0則會渲染為0
  • key 需要滿足的條件
    • key 值在兄弟節(jié)點之間必須是唯一的。 不過不要求全局唯一,在不同的數(shù)組中可以使用相同的 key。
    • key 值不能改變,否則就失去了使用 key 的意義!所以千萬不要在渲染時動態(tài)地生成 key。
  • 為什么需要key
    • React 里需要 key 和文件夾里的文件需要有文件名的道理是類似的。一個精心選擇的 key 值所能提供的信息遠遠不止于這個元素在數(shù)組中的位置。即使元素的位置在渲染的過程中發(fā)生了改變,它提供的 key 值也能讓 React 在整個生命周期中一直認得它。
    • 請不要在運行過程中動態(tài)地產生 key,像是 key={Math.random()} 這種方式。這會導致每次重新渲染后的 key 值都不一樣,從而使得所有的組件和 DOM 元素每次都要重新創(chuàng)建。這不僅會造成運行變慢的問題,更有可能導致用戶輸入的丟失。
  • 純函數(shù)的基本定義:
    • 只負責自己的任務。 它不會更改在該函數(shù)調用前就已存在的對象或變量。
    • 輸入相同,輸出也相同。 在輸入相同的情況下,對純函數(shù)來說應總是返回相同的結果。
  • React 假設你編寫的所有組件都是純函數(shù)。React 的渲染過程必須自始至終是純粹的。組件應該只返回它們的 JSX,而不改變在渲染前,就已存在的任何對象或變量 — 這將會使它們變得不純粹!
  • React 提供了 “嚴格模式”,在嚴格模式下開發(fā)時,它將會調用每個組件函數(shù)兩次。通過重復調用組件函數(shù),嚴格模式有助于找到違反這些規(guī)則的組件。
  • 副作用:包括更新屏幕、啟動動畫、更改數(shù)據(jù)等,它們被稱為 副作用。它們是 “額外” 發(fā)生的事情,與渲染過程無關。
  • 在 React 中,副作用通常屬于 事件處理程序。事件處理程序是 React 在你執(zhí)行某些操作(如單擊按鈕)時運行的函數(shù)。即使事件處理程序是在你的組件 內部 定義的,它們也不會在渲染期間運行! 因此事件處理程序無需是純函數(shù)。
  • 如果你用盡一切辦法,仍無法為副作用找到合適的事件處理程序,你還可以調用組件中的 useEffect 方法將其附加到返回的 JSX 中。這會告訴 React 在渲染結束后執(zhí)行它。然而,這種方法應該是你最后的手段

添加交互

  • 事件處理函數(shù)是執(zhí)行副作用的最佳位置,數(shù)據(jù)的改變使用state存儲

  • 為什么需要useState

    • 局部變量無法在多次渲染中持久保存。當 React 再次渲染這個組件時,它會從頭開始渲染——不會考慮之前對局部變量的任何更改。
    • 更改局部變量不會觸發(fā)渲染。 React 沒有意識到它需要使用新數(shù)據(jù)再次渲染組件。
  • Hooks ——以 use 開頭的函數(shù)——只能在組件或自定義 Hook 的最頂層調用。

  • 如果你渲染同一個組件兩次,每個副本都會有完全隔離的 state

  • React 把更改提交到 DOM 上

    • 對于初次渲染, React 會使用 appendChild() DOM API 將其創(chuàng)建的所有 DOM 節(jié)點放在屏幕上。
    • 對于重渲染, React 將應用最少的必要操作(在渲染時計算!),以使得 DOM 與最新的渲染輸出相互匹配。
  • React 僅在渲染之間存在差異時才會更改 DOM 節(jié)點

  • 在渲染完成并且 React 更新 DOM 之后,瀏覽器就會重新繪制屏幕

  • 在一個 React 應用中一次屏幕更新都會發(fā)生以下三個步驟:

    1. 觸發(fā)
    • 組件的 初次渲染。
    • 組件(或者其祖先之一)的 狀態(tài)發(fā)生了改變
    1. 渲染。在您觸發(fā)渲染后,React 會調用您的組件來確定要在屏幕上顯示的內容?!颁秩局小?即 React 在調用您的組件。
    • 在進行初次渲染時, React 會調用根組件。
    • 對于后續(xù)的渲染, React 會調用內部狀態(tài)更新觸發(fā)了渲染的函數(shù)組件。
    1. 提交。在渲染(調用)您的組件之后,React 將會修改 DOM。
    • 對于初次渲染, React 會使用 appendChild() DOM API 將其創(chuàng)建的所有 DOM 節(jié)點放在屏幕上。
    • 對于重渲染, React 將應用最少的必要操作(在渲染時計算?。允沟?DOM 與最新的渲染輸出相互匹配。
  • 設置 state 會觸發(fā)渲染(當你調用 useState 時)

  • 渲染會及時生成一張快照

    • “正在渲染” 就意味著 React 正在調用你的組件——一個函數(shù)。你從該函數(shù)返回的 JSX 就像是 UI 的一張及時的快照。它的 props、事件處理函數(shù)和內部變量都是 根據(jù)當前渲染時的 state 被計算出來的。
    • 返回的 UI “快照”是可交互的。它其中包括類似事件處理函數(shù)的邏輯,這些邏輯用于指定如何對輸入作出響應。React 隨后會更新屏幕來匹配這張快照,并綁定事件處理函數(shù)。因此,按下按鈕就會觸發(fā)你 JSX 中的點擊事件處理函數(shù)。
    • state 實際上“活”在 React 本身中——就像被擺在一個架子上!——位于你的函數(shù)之外。當 React 調用你的組件時,它會為特定的那一次渲染提供一張 state 快照。你的組件會在其 JSX 中返回一張包含一整套新的 props 和事件處理函數(shù)的 UI 快照 ,其中所有的值都是 根據(jù)那一次渲染中 state 的值 被計算出來的!
  • 設置 state 只會為下一次渲染變更 state 的值。

  • 一個 state 變量的值永遠不會在一次渲染的內部發(fā)生變化, 即使其事件處理函數(shù)的代碼是異步的。

  • React 會使 state 的值始終”固定“在一次渲染的各個事件處理函數(shù)內部。 你無需擔心代碼運行時 state 是否發(fā)生了變化。

  • 變量和事件處理函數(shù)不會在重渲染中“存活”。每個渲染都有自己的事件處理函數(shù)。

  • 每個渲染(以及其中的函數(shù))始終“看到”的是 React 提供給這個 渲染的 state 快照。

  • 過去創(chuàng)建的事件處理函數(shù)擁有的是創(chuàng)建它們的那次渲染中的 state 值。

  • React 會等到事件處理函數(shù)中的 所有 代碼都運行完畢再處理你的 state 更新。

  • 在下次渲染前多次更新同一個 state,需要傳入一個更新函數(shù),如setNumber(n => n + 1),而不是像 setNumber(number + 1) 這樣傳入 下一個 state 值

  • 設置 state 不會更改現(xiàn)有渲染中的變量,但會請求一次新的渲染。

  • React 會在事件處理函數(shù)執(zhí)行完成之后處理 state 更新。這被稱為批處理。

  • 要在一個事件中多次更新某些 state,你可以使用 setNumber(n => n + 1) 更新函數(shù)。

  • 把所有存放在 state 中的 JavaScript 對象都視為只讀的,為了真正地 觸發(fā)一次重新渲染你需要創(chuàng)建一個新對象并把它傳遞給 state 的設置函數(shù)

  • 不要直接修改一個對象,而要為它創(chuàng)建一個 新 版本,并通過把 state 設置成這個新版本來觸發(fā)重新渲染。

  • 你可以使用這樣的 {...obj, something: 'newValue'} 對象展開語法來創(chuàng)建對象的拷貝。

狀態(tài)管理

  • 只要一個組件還被渲染在 UI 樹的相同位置,React 就會保留它的 state。 如果它被移除,或者一個不同的組件被渲染在相同的位置,那么 React 就會丟掉它的 state。
  • 相同位置的相同組件會使得 state 被保留下來
  • 對 React 來說重要的是組件在 UI 樹中的位置,而不是在 JSX 中的位置!
  • 當你在相同位置渲染不同的組件時,組件的整個子樹都會被重置
  • 永遠要將組件定義在最上層(函數(shù)組件外)并且不要把它們的定義嵌套起來,否則每次渲染出的都是不同的組件,狀態(tài)會丟失
  • 在相同位置重置 state
    1. 將組件渲染在不同的位置。同一個組件在第一個位置與第二個位置交替展示時會重置state
    2. 使用 key 來重置 state。即使兩個 <Counter /> 會出現(xiàn)在 JSX 中的同一個位置,它們也不會共享 state
    • 使用key后,React 將重新創(chuàng)建 DOM 元素,而不是復用它們。
  • 為被移除的組件保留 state
    1. 用 CSS 把其他組件隱藏起來。這些組件就不會從樹中被移除了,所以它們的內部 state 會被保留下來。這種解決方法對于簡單 UI 非常有效。但如果要隱藏的樹形結構很大且包含了大量的 DOM 節(jié)點,那么性能就會變得很差。
    2. 你可以進行狀態(tài)提升并在父組件中保存每個收件人的草稿消息。這是最常見的解決方法
    3. 你也可以使用localStorage存儲草稿信息
    4. 為多個 不同位置的 相同組件 指定key可以保留state
  • 把 useState 轉化為 useReducer:
    1. 通過事件處理函數(shù) dispatch actions(派發(fā)action對象);
    2. 編寫一個 reducer 函數(shù),它接受傳入的 state 和一個 action對象(通常包含type字段),并返回一個新的 state;
    3. 使用 useReducer 替換 useState;
  • reducer 必須是一個純函數(shù)——它應該只計算下一個狀態(tài)。而不應該 “做” 其它事情,包括向用戶顯示消息。這應該在事件處理程序中處理。(為了便于捕獲這樣的錯誤,React 會在嚴格模式下多次調用你的 reducer。
  • Context 可以讓父節(jié)點,甚至是很遠的父節(jié)點都可以為其內部的整個組件樹提供數(shù)據(jù)。
    1. 通過 export const MyContext = createContext(defaultValue) 創(chuàng)建并導出 context。
    2. 在無論層級多深的任何子組件中,把 context 傳遞給 useContext(MyContext) Hook 來讀取它。
    3. 在父組件中把 children 包在 <MyContext.Provider value={...}> 中來提供 context。
  • 在 React 中,覆蓋來自上層的某些 context 的唯一方法是將子組件包裹到一個提供不同值的 context provider 中。
  • 不同的 React context 不會覆蓋彼此。你通過 createContext() 創(chuàng)建的每個 context 都和其他 context 完全分離,只有使用和提供 那個特定的 context 的組件才會聯(lián)系在一起。一個組件可以輕松地使用或者提供許多不同的 context。

應急方案

  • 當你希望組件“記住”某些信息,但又不想讓這些信息觸發(fā)新的渲染時,你可以使用 ref 。
  • 當一條信息用于渲染時,將它保存在 state 中。當一條信息僅被事件處理器需要,并且更改它不需要重新渲染時,使用 ref 可能會更高效。
  • 何時使用ref
    • 存儲timeout ID
    • 存儲和操作DOM 元素
    • 存儲不需要被用來計算 JSX 的其他對象。
    • 不要在渲染過程中讀取或寫入 ref.current,ref 本身是一個普通的 JavaScript 對象
  • 給未知長度的列表中每項綁定ref
    • 將函數(shù)傳遞給 ref 屬性。這稱為 ref 回調。當需要設置 ref 時,React 將傳入 DOM 節(jié)點來調用你的 ref 回調,并在需要清除它時傳入 null 。這使你可以維護自己的數(shù)組或 Map,并通過其索引或某種類型的 ID 訪問任何 ref。
  • 訪問另一個組件的 DOM 節(jié)點
    • 一個組件可以指定將它的 ref “轉發(fā)”給一個子組件,子組件是使用 forwardRef 聲明的。 這讓從父組件接收的ref作為第二個參數(shù)傳入組件,第一個參數(shù)是 props
  • 使用ref后,父組件可以調用子組件dom節(jié)點的所有方法,使用命令句柄(useImperativeHandle)可以只暴露一部分 API
  • 通常,你將從事件處理器訪問 refs。
  • 你可以強制 React 同步更新(“刷新”)DOM(添加一個元素后滾動至該元素)。 為此,從 react-dom 導入 flushSync 并將 state 更新包裹 到 flushSync 調用中
  • useEffect 會把它包裹的代碼放到屏幕更新渲染之后執(zhí)行
    -為什么依賴數(shù)組中可以省略 ref 或者 set函數(shù)?
  • React 總是在執(zhí)行下一輪渲染的 Effect 之前清理(執(zhí)行effect中return出的清理函數(shù))上一輪渲染的 Effect。
  • 如果一個值可以基于現(xiàn)有的 props 或 state 計算得出,不要把它作為一個 state,而是在渲染期間直接計算這個值
  • 你可以使用 useMemo Hook 緩存(或者說 記憶(memoize))一個昂貴的計算(用該hook包裹一個耗時的函數(shù),以避免每次渲染期間的重新計算)。
  • 當 props 變化時重置所有 state,應當給該組件設置key屬性,key變化,react會自動重置該組件
  • 當 props 變化時重置部分 state
    • 在渲染期間更新組件時,React 會丟棄已經(jīng)返回的 JSX 并立即嘗試重新渲染。為了避免非常緩慢的級聯(lián)重試,React 只允許在渲染期間更新 同一 組件的狀態(tài)。如果你在渲染期間更新另一個組件的狀態(tài),你會看到一條報錯信息。
  • 如果某些邏輯必須在 每次應用加載時執(zhí)行一次,而不是在 每次組件掛載時執(zhí)行一次,可以添加一個頂層變量(寫在組件函數(shù)之外)來記錄它是否已經(jīng)執(zhí)行過了
  • 訂閱外部 store,useSyncExternalStore
  • 數(shù)據(jù)請求中,為了修復競態(tài)條件下返回值set錯誤的問題,你需要添加一個 清理函數(shù) 來忽略較早的返回結果
  useEffect(() => {
    let ignore = false;
    fetchResults(query, page).then(json => {
      if (!ignore) {
        setResults(json);
      }
    });
    return () => {
      ignore = true;
    };
  }, [query, page]);
  • 每個 Effect 表示一個獨立的同步過程。抵制將與 Effect 無關的邏輯添加到已經(jīng)編寫的 Effect 中(代碼中的每個 Effect 應該代表一個獨立的同步過程),僅僅因為 這些邏輯 需要與 Effect 同時運行(應重新寫一個effect函數(shù)來同步 這些邏輯,它的的好處是,刪除一個 Effect 不會影響另一個 Effect 的邏輯)。
  • 組件內部的所有值(包括 props、state 和組件體內的變量)都是響應式的。任何響應式值都可以在重新渲染時發(fā)生變化,所以需要將響應式值包括在 Effect 的依賴項中。
  • 避免將對象和函數(shù)作為依賴項。如果在渲染過程中創(chuàng)建對象和函數(shù),然后在 Effect 中讀取它們,它們將在每次渲染時都不同。這將導致 Effect 每次都重新同步
  • 避免禁用檢查工具。為了不違反規(guī)則,修復代碼總是值得的!
useEffect(() => {
  // ...
  // ?? 避免這樣禁用靜態(tài)代碼分析工具:
  // eslint-ignore-next-line react-hooks/exhaustive-deps
}, []);
  • 使用 useEffectEvent (實驗性的功能)這個特殊的 Hook 從 Effect 中提取非響應式邏輯。Effect Event。它是 Effect 邏輯的一部分,但是其行為更像事件處理函數(shù)。它內部的邏輯不是響應式的,不需要添加到effect的依賴中,Effect Event 讓你在 Effect 響應性和不應是響應式的代碼間“打破鏈條”, Effect Event 讀取的是最新的 props 和 state 。
  • 事件處理函數(shù)內部的邏輯是非響應式的(用戶的特定操作,才會執(zhí)行)。
  • Effect 內部的邏輯是響應式的(切換聊天室,需要自動斷開之前的,連接最新的)。
  • 你可以將非響應式邏輯從 Effect 移到 Effect Event 中。
  • 只在 Effect 內部調用 Effect Event。
  • 不要將 Effect Event 傳給其他組件或者 Hook。
  • 注意,你不能“選擇” Effect 的依賴。每個被 Effect 所使用的響應式值,必須在依賴中聲明。依賴是由 Effect 的代碼決定的。每個 Effect 應該代表一個獨立的同步過程,不應該同步兩個不同的、不相關的東西,應將他們拆分在不同的effect中
  • 是否在讀取一些狀態(tài)來計算下一個狀態(tài)?使用state的更新函數(shù)寫法
  • 你想讀取一個值而不對其變化做出“反應”嗎?或者來自 props 的事件處理程序的處理,將非響應式邏輯(props的事件處理程序)移至 Effect Event 中(非響應式邏輯應該在事件中)
  • 盡量避免對象和函數(shù)依賴。將它們移到組件外(常量對象或者函數(shù))、effect外(在effect外對對象或者函數(shù)解構,使effect依賴解構出來的基本類型的值)、 Effect 內(在effect中創(chuàng)建對象或者函數(shù))
  • 自定義 Hook 共享的只是狀態(tài)邏輯而不是狀態(tài)本身。對 Hook 的每個調用完全獨立于對同一個 Hook 的其他調用
  • 不要創(chuàng)建像 useMount 這樣的自定義 Hook。保持目標具體化。

api

  • 即使一個組件被記憶化(memo)了,當它自身的狀態(tài)發(fā)生變化時,它仍然會重新渲染
  • 即使組件已被記憶化(memo),當其使用的 context 發(fā)生變化時,它仍將重新渲染
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容