成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

React新文檔:不要濫用Ref哦!

開發 前端
React新文檔有個很有意思的細節:useRef、useEffect這兩個API的介紹,在文檔中所在的章節叫Escape Hatches(逃生艙)。

大家好,我卡頌。

React新文檔有個很有意思的細節:useRef、useEffect這兩個API的介紹,在文檔中所在的章節叫Escape Hatches(逃生艙)。

顯然,正常航行時是不需要逃生艙的,只有在遇到危險時會用到。

如果開發者過多依賴這兩個API,可能是誤用。

??在React新文檔:不要濫用effect哦??中我們談到useEffect的正確使用場景。

今天,我們來聊聊Ref的使用場景。

為什么是逃生艙?

先思考一個問題:為什么ref、effect被歸類到「逃生艙」中?

這是因為二者操作的都是「脫離React控制的因素」。

effect中處理的是「副作用」。比如:在useEffect中修改了document.title。

document.title不屬于React中的狀態,React無法感知他的變化,所以被歸類到effect中。

同樣,「使DOM聚焦」需要調用element.focus(),直接執行DOM API也是不受React控制的。

雖然他們是「脫離React控制的因素」,但為了保證應用的健壯,React也要盡可能防止他們失控。

失控的Ref

對于Ref,什么叫失控呢?

首先來看「不失控」的情況:

  • 執行ref.current的focus、blur等方法。
  • 執行ref.current.scrollIntoView使element滾動到視野內。
  • 執行ref.current.getBoundingClientRect測量DOM尺寸。

這些情況下,雖然我們操作了DOM,但涉及的都是「React控制范圍外的因素」,所以不算失控。

但是下面的情況:

  • 執行ref.current.remove移除DOM。
  • 執行ref.current.appendChild插入子節點。

同樣是操作DOM,但這些屬于「React控制范圍內的因素」,通過ref執行這些操作就屬于失控的情況。

舉個例子,下面是React文檔中的例子[1]:

「按鈕1」點擊后會插入/移除 P節點,「按鈕2」點擊后會調用DOM API移除P節點:

export default function Counter() {
const [show, setShow] = useState(true);
const ref = useRef(null);
return (
<div>
<button
onClick={() => {
setShow(!show);
}}>
Toggle with setState
</button>
<button
onClick={() => {
ref.current.remove();
}}>
Remove from the DOM
</button>
{show && <p ref={ref}>Hello world</p>}
</div>
);
}

「按鈕1」通過React控制的方式移除P節點。

「按鈕2」直接操作DOM移除P節點。

如果這兩種「移除P節點」的方式混用,那么先點擊「按鈕1」再點擊「按鈕2」就會報錯:

圖片

這就是「使用Ref操作DOM造成的失控情況」導致的。

如何限制失控

現在問題來了,既然叫「失控」了,那就是React沒法控制的(React總不能限制開發者不能使用DOM API吧?),那如何限制失控呢?

在React中,組件可以分為:

  • 高階組件
  • 低階組件

「低階組件」指那些「基于DOM封裝的組件」,比如下面的組件,直接基于input節點封裝:

function MyInput(props) {
return <input {...props} />;
}

在「低階組件」中,是可以直接將ref指向DOM的,比如:

function MyInput(props) {
const ref = useRef(null);
return <input ref={ref} {...props} />;
}

「高階組件」指那些「基于低階組件封裝的組件」,比如下面的Form組件,基于Input組件封裝:

function Form() {
return (
<>
<MyInput/>
</>
)
}

「高階組件」無法直接將ref指向DOM,這一限制就將「ref失控」的范圍控制在單個組件內,不會出現跨越組件的「ref失控」。

以文檔中的示例[2]為例,如果我們想在Form組件中點擊按鈕,操作input聚焦:

function MyInput(props) {
return <input {...props} />;
}
function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
input聚焦
</button>
</>
);
}

點擊后,會報錯:

圖片

這是因為在Form組件中向MyInput傳遞ref失敗了,inputRef.current并沒有指向input節點。

究其原因,就是上面說的「為了將ref失控的范圍控制在單個組件內,React默認情況下不支持跨組件傳遞ref」。

人為取消限制

如果一定要取消這個限制,可以使用forwardRef API顯式傳遞ref:

const MyInput = forwardRef((props, ref) => {
return <input {...props} ref={ref} />;
});
function Form() {
const inputRef = useRef(null);
function handleClick() {
inputRef.current.focus();
}
return (
<>
<MyInput ref={inputRef} />
<button onClick={handleClick}>
Focus the input
</button>
</>
);
}

使用forwardRef(forward在這里是「傳遞」的意思)后,就能跨組件傳遞ref。

在例子中,我們將inputRef從Form跨組件傳遞到MyInput中,并與input產生關聯。

在實踐中,一些同學可能覺得forwardRef這一API有些多此一舉。

但從「ref失控」的角度看,forwardRef的意圖就很明顯了:既然開發者手動調用forwardRef破除「防止ref失控的限制」,那他應該知道自己在做什么,也應該自己承擔相應的風險。

同時,有了forwardRef的存在,發生「ref相關錯誤」后也更容易定位錯誤。

useImperativeHandle

除了「限制跨組件傳遞ref」外,還有一種「防止ref失控的措施」,那就是useImperativeHandle,他的邏輯是這樣的:

既然「ref失控」是由于「使用了不該被使用的DOM方法」(比如appendChild),那我可以限制「ref中只存在可以被使用的方法」。

用useImperativeHandle修改我們的MyInput組件:

const MyInput = forwardRef((props, ref) => {
const realInputRef = useRef(null);
useImperativeHandle(ref, () => ({
focus() {
realInputRef.current.focus();
},
}));
return <input {...props} ref={realInputRef} />;
});

現在,Form組件中通過inputRef.current只能取到如下數據結構:

{
focus() {
realInputRef.current.focus();
},
}

就杜絕了「開發者通過ref取到DOM后,執行不該被使用的API,出現ref失控」的情況。

總結

正常情況,Ref的使用比較少,他是作為「逃生艙」而存在的。

為了防止錯用/濫用導致ref失控,React限制「默認情況下,不能跨組件傳遞ref」。

為了破除這種限制,可以使用forwardRef。

為了減少ref對DOM的濫用,可以使用useImperativeHandle限制ref傳遞的數據結構。

參考資料

[1]React文檔中的例子:https://codesandbox.io/s/sandpack-project-forked-s33q3c。

[2]文檔中的示例:https://codesandbox.io/s/sandpack-project-forked-7zqgmd。


責任編輯:姜華 來源: 魔術師卡頌
相關推薦

2022-06-05 21:27:40

Reacteffect

2021-10-22 15:45:32

開發技能React

2022-05-27 16:03:37

ReactDanVue

2021-05-11 07:51:30

React ref 前端

2023-03-20 07:12:54

GPT學習React

2023-11-07 11:24:18

2012-02-20 09:56:55

ibmdw

2009-03-11 08:46:46

Chrome瀏覽器更新

2023-01-05 08:34:48

JDK工具

2020-10-19 10:25:57

ReactReact.js前端

2022-07-11 06:56:00

Reactreact-dom

2022-05-20 15:27:41

React工具Vue

2023-04-11 16:31:10

開發React 庫Web

2023-03-19 11:42:19

React新官方文檔

2021-02-08 18:25:52

微軟WindowsWindows 10

2012-02-16 13:36:39

JavaJavaFX

2023-05-19 10:04:18

Vue開發者代碼

2021-08-06 09:30:34

人工智能AI人臉識別

2013-03-21 17:42:35

2024-06-03 08:32:54

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 91免费在线视频 | 国产精品不卡视频 | 久久免费视频1 | 91一区二区三区在线观看 | av网站免费看 | 日韩精品视频在线 | 成人免费视频在线观看 | 久久一| 97天天干| 欧美成人不卡 | 老司机狠狠爱 | 一级大黄 | 97超碰免费 | 国产精品成人一区二区三区 | 亚洲色图网址 | 日本精品视频 | 日韩欧美三级电影 | 日韩欧美一区二区三区在线播放 | 成人免费区一区二区三区 | 欧美精品一区二区免费视频 | 欧美精品欧美精品系列 | 精品久久久久久久人人人人传媒 | 精品一区二区三区在线观看 | 亚av在线| 亚洲精品一区二区三区蜜桃久 | 一区二区日韩 | 日本a∨精品中文字幕在线 亚洲91视频 | 亚洲+变态+欧美+另类+精品 | www.99re5.com| 最新国产精品精品视频 | 999www视频免费观看 | 国产精品高清在线 | 国产亚洲网站 | 精品久久久久久久久久久院品网 | 久久久精品在线 | 热久久久 | 亚洲国产精品va在线看黑人 | 亚洲成人三区 | 成年网站在线观看 | www.国产精| 国产剧情久久 |