欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

淺談React?Refs?使用場景及核心要點

 更新時間:2022年06月20日 10:53:05   作者:行走的烏龜  
本文主要介紹了React?Refs?使用場景及核心要點,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧

在使用 React 進行開發(fā)過程中,或多或少使用過 Refs 進行 DOM 操作或者訪問一些DOM上的API,又或使用 Refs 保存數據。不管怎么說 Refs 總是 React 提供的一大助力,這篇文章主要介紹 Refs 功能和使用場景以及注意事項。希望能增強對 Refs 的理解,掌握好這把利劍。

什么是 Refs?

Refs 是 React 提供的用來保存 object 引用的一個解決方案,在函數式組件使用 useRef 創(chuàng)建一個 ref 對象,ref 對象存在一個可直接修改的 current 屬性,內容都是存在 current 上。Refs 使用場景主要分為兩個方向,其一是實現 DOM 訪問與操控、在兩次render之間傳遞數據內容【和state機制有很大不同,下文會有對比介紹】。如果在組件返回的 jsx dom上綁定了 ref 屬性,React 在處理 jsx 時會把該dom節(jié)點【原生node節(jié)點】的引用存儲在 ref.current 上。

使用方式

分為三步:

  • 第一步、使用 useRef 創(chuàng)建 ref 對象(useRef 是 FC hooks, class 組件使用 React.createRef() 創(chuàng)建 )
  • 第二步、賦值&使用【操作dom則綁定為dom的ref屬性的值,用于保存值的時候傳遞內容給 ref.current】,
  • 第三步、訪問ref內容【進行dom對應的api訪問,進行 scroll 、focus等操作。又或者從current中讀取保存的數據】最終的目的還是最后訪問拿到對應的數據進行操作。下邊我們分別用兩個小 demo 簡單先看看用法,理論和總結在后邊一點。

Example one 實現點擊按鈕 focus input 框

import React, { useRef } from "react";

export default function Comp() {
  // 第一步:使用 useRef 創(chuàng)建一個 ref 對象 { current: null }
  const ref = useRef();

  function handleClick() {
    // 第三步:訪問到 ref 上存的內容,這里是 input 的node節(jié)點
    ref.current.focus();
  }

  // 第二步:賦值 ref
  return (
    <>
      <input ref={ref} />
      <button onClick={handleClick}>開始輸入</button>
    </>
  );
}

Example two 實現數據發(fā)送3s內撤回功能:在點擊發(fā)送后3s內如果點擊 “取消發(fā)送” 則取消本次發(fā)送

簡單起見我們按鈕不實際發(fā)送請求,定時 3s 如果3s內點擊了 “取消發(fā)送”則取消發(fā)送。發(fā)送功能用提醒 “已發(fā)送”代替,出現“已發(fā)送”表示執(zhí)行了發(fā)送。

import React, { useRef, useState } from "react";

export default function CompA() {
  // 第一步:使用useRef 創(chuàng)建 ref 對象
  const ref = useRef();
  const [isSending, setIsSending] = useState(false);

  function send() {
    // ...
    window.alert("消息已發(fā)送!");
    setIsSending(false);
  }

  function undo() {
    // 第三步: 訪問存在 ref 上的 timeout ID, 進行定時取消
    clearTimeout(ref.current);
  }

  function handleClickSendBtn() {
    setIsSending(true);
    // 第二步: 賦值,將 timeout ID 存在 ref 上
    ref.current = setTimeout(send, 3000);
  }

  function handleClickCancelBtn() {
    undo();
    setIsSending(false);
  }

  return (
    <>
      <button onClick={handleClickSendBtn} disabled={isSending}>
        {isSending ? "發(fā)送中..." : "發(fā)送"}
      </button>
      {isSending && <button onClick={handleClickCancelBtn}>取消發(fā)送</button>}
    </>
  );
}

Refs 核心要點

我們通過兩個簡單 case 演示了一下,DOM 操作 以及用于在兩次 re-render 之間傳遞內容(case 2 傳遞的內容是 timeout 的ID)。在使用 Refs 的過程中有幾點尤其需要注意。

避免重復創(chuàng)建 ref 內容

在使用 useRef 進行創(chuàng)建 ref 時可以傳遞 null、number 、object 等內容也可以傳遞初始化函數。React 只會保存一次初始值,并把它帶到下一次 render 中。因此在 useRef 在創(chuàng)建ref的時候傳遞重復的內容是不生效的,如果你認為每次都生成一個新的值賦給ref但是React給你的卻是第一次傳遞的值,這可能不符合你的預期。

import React, { useEffect, useRef, useState } from "react";

export function CompB() {
  // 注意: 這個部分不會每次生成一個新的時間戳,只會采用 mounted 時新建的第一次時間戳。
  const ref = useRef(+new Date());
  
  const [count, setCount] = useState(0);

  useEffect(() => {
    console.log(`第${count}渲染時間:`, +new Date());
    console.log("ref", ref.current);
  });

  function handleClick() {
    // 為了讓點擊時,更新 state 觸發(fā) re-render
    setCount(count + 1);
  }

  return (
    <>
      <button onClick={handleClick}>點擊讓組件渲染</button>
    </>
  );
}

注意:效果圖中初始化的時候會打印兩次重復的第0次渲染,是因為 React 在 dev 模式下會執(zhí)行兩遍組件內容,檢測組件是否是純組件。并非代碼問題,后續(xù)研讀源碼時會有文章介紹,歡迎關注。

ref.current 存儲的內容修改是突變

對于 state 來說,直接修改state不會生效。需要使用 useState 給的第二個返回值來進行修改。而 ref 則是可直接修改 current 屬性上的內容,并且修改后可以立即取到值。ref存儲的實際就是一個引用,因此是可突變的。

import React, { useRef } from 'react';
export function Comp() {
  const ref = useRef(0);
  
  useEffect(()=>{
    console.log(ref.current); // 0
    // 突變
    ref.current = ref.current + 1;
    console.log(ref.current); // 1
  });
  
  return <div></div>
}

ref 作為數據存儲時內容的變化不會引起 re-render

React 組件的 re-render 的觸發(fā)一般是【state、props、context】中的出現變化引起的。修改 Ref 的內容不會引起組件的 re-render 因此不能用 ref 去干預 React 生成jsx。換句話說就是不能用在jsx中做渲染或者條件判斷,不然可能得到沒辦法預料的jsx結果。

ref 的讀寫只能在 useEffect 或者回調函數中進行

React 約定 state、props、context 都是一樣的就應該輸出同樣的jsx內容,只要這三個要素不變那么以不同的調用順序執(zhí)行組件應該得到同樣的結果。要說清楚為什么Ref的讀寫只能在useEffect和回調函數中,得先鋪墊一下React的一些架構知識。

React 架構上分為三個部分【調度器Scheduler、協調器Reconciler、渲染器Renderer】,整體上又是兩個階段【render 階段,commit階段】。render 階段的目的是找出哪些組件需要更新,以及如何更新(這些內容會標記在Fiber節(jié)點上)【更新過程可中斷可搶占的,高優(yōu)的更新可搶占優(yōu)先先執(zhí)行。這個階段主要是 Scheduler 負責調度優(yōu)先級, 協調器負責找出更新的內容并標記好】,commit 階段的作用用一句話就是【根據 render 階段標記的結果Fiber上的tag,操作dom,執(zhí)行 useEffect 以及對應階段的生命周期函數】。

在 render 階段會執(zhí)行組件,如果出現高優(yōu)更新搶占,那么低優(yōu)先級的更新在高優(yōu)更新執(zhí)行完成后會重新執(zhí)行一遍【函數式組件也就是個function函數,在函數體中間的執(zhí)行 ref 寫操作會被多次執(zhí)行】,我們會發(fā)現如果ref的賦值操作在這個期間執(zhí)行了那么組件更新的結果就是不可預期的【未被搶占時ref的結果是1,被搶占1次時是2。這完全是不可預期大的】。而 useEffect 或者回調函數都不是在 render 階段執(zhí)行的因此每次更新只執(zhí)行一次。也就是說ref的讀寫不能出現在render階段,就只能寫在 useEffect【類組件對應的是生命周期函數,注意不能寫在 componentWillxxx 生命周期中,因為 componentWillxxx 生命周期函數執(zhí)行在 render 階段】和回調函數中。

// bad
function MyComponent() {
  // ...
  // ?? Don't write a ref during rendering
  myRef.current = 123;
  // ...
  // ?? Don't read a ref during rendering
  return <h1>{myOtherRef.current}</h1>;
}


// good
function MyComponent() {
  // ...
  useEffect(() => {
    // ? You can read or write refs in effects
    myRef.current = 123;
  });
  // ...
  function handleClick() {
    // ? You can read or write refs in event handlers
    doSomething(myOtherRef.current);
  }
  // ...
}

跨組件傳遞ref 獲取dom時需要借助 forwardRef 包裹組件

React 默認情況下不允許組件訪問其他組件的dom節(jié)點,因此關閉了直接 props 傳遞 ref 標記組件的dom這種操作。得借助 React.forwardRef api 傳遞 實現這種跨組件的dom操作。

import React, { useEffect, useRef, useState, forwardRef } from "react";

export function ParentComp() {
  const childInputRef = useRef(null);

  function handleClick() {
    childInputRef.current?.focus();
  }

  return (
    <>
      <button onClick={handleClick}>編輯</button>
      <ChildComp ref={childInputRef} />
    </>
  );
}

// 使用forwordRef 包裹組件,接受 ref 并轉發(fā)綁定到對應dom上
const ChildComp = forwardRef((props, ref) => {
  return (
    <div>
      <input {...props} ref={ref} />
    </div>
  );
});

ref 綁定的dom在離屏或者未掛載時ref.current 值會被修改為null

ref 綁定的dom在離屏或者未掛載時ref.current 值會被修改為 null 。如果在組件中間會進行條件渲染,那么需要處理一下判斷邏輯,不然代碼可能會拋出異常。另外在父組件引用子組件 dom 的場景也應該增加對 null 的判斷。至此 Refs 的要點已經介紹完成。

最佳實踐

接下來我們接著聊聊什么情況下使用 Refs 比較好,React 官方把 Refs 定義為逃生通道,就是暗示要謹慎使用。

dom 操作相關

  • 如果需要進行焦點管理、位置滾動等非破壞性行為以及調用 dom 節(jié)點的 api 那么推薦使用 Refs。
  • 如果是為了修改 dom ,比如修改dom屬性,標簽名稱等等,可能會與 React 存在沖突,不推薦這樣使用Refs 而是應該換種思路考慮使用 state 進行條件渲染。

用于在兩次 render 之間傳遞數據

  • 如果組件中大部分功能都依賴該數據,那么不應該存放在ref中。
  • 如果數據在jsx中使用,那么不推薦放在ref中, 這會帶來問題【詳見:ref 的讀寫只能在 useEffect 或者回調函數中進行】,推薦使用 useState。
  • 想要保存數據并且不希望數據變化時引起組件 re-render, 而只是在回調函數中需要獲取到對應內容時,推薦使用 Ref。如 interval id 。

到此這篇關于淺談React Refs 使用場景及核心要點的文章就介紹到這了,更多相關React Refs 使用場景 內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!

相關文章

  • react  Suspense工作原理解析

    react  Suspense工作原理解析

    這篇文章主要為大家介紹了react  Suspense工作原理解析以及基本應用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • 淺談對于react-thunk中間件的簡單理解

    淺談對于react-thunk中間件的簡單理解

    這篇文章主要介紹了淺談對于react-thunk中間件的簡單理解,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-05-05
  • TypeScript在React中的應用技術實例解析

    TypeScript在React中的應用技術實例解析

    這篇文章主要為大家介紹了TypeScript在React中的應用技術實例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-04-04
  • react中引入less并支持antd主題換膚方式

    react中引入less并支持antd主題換膚方式

    這篇文章主要介紹了react中引入less并支持antd主題換膚方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-01-01
  • 詳解如何封裝和使用一個React鑒權組件

    詳解如何封裝和使用一個React鑒權組件

    JavaScript?和?React?提供了靈活的事件處理機制,特別是通過利用事件的捕獲階段來阻止事件傳播可以實現精細的權限控制,本文將詳細介紹這一技術的應用,并通過實踐案例展示如何封裝和使用一個?React?鑒權組件,需要的可以參考下
    2024-03-03
  • React Fiber中面試官最關心的技術話題

    React Fiber中面試官最關心的技術話題

    這篇文章主要為大家介紹了React Fiber中面試官最關心的技術話題解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2023-06-06
  • 在react-antd中彈出層form內容傳遞給父組件的操作

    在react-antd中彈出層form內容傳遞給父組件的操作

    這篇文章主要介紹了在react-antd中彈出層form內容傳遞給父組件的操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-10-10
  • 如何用react優(yōu)雅的書寫CSS

    如何用react優(yōu)雅的書寫CSS

    這篇文章主要介紹了如何用react優(yōu)雅的書寫CSS,幫助大家更好的理解和學習使用react,感興趣的朋友可以了解下
    2021-04-04
  • Reactjs?+?Nodejs?+?Mongodb?實現文件上傳功能實例詳解

    Reactjs?+?Nodejs?+?Mongodb?實現文件上傳功能實例詳解

    今天是使用?Reactjs?+?Nodejs?+?Mongodb?實現文件上傳功能,前端我們使用?Reactjs?+?Axios?來搭建前端上傳文件應用,后端我們使用?Node.js?+?Express?+?Multer?+?Mongodb?來搭建后端上傳文件處理應用,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2022-06-06
  • Webpack3+React16代碼分割的實現

    Webpack3+React16代碼分割的實現

    這篇文章主要介紹了Webpack3+React16代碼分割的實現,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2021-03-03

最新評論