React報錯之Object is possibly null
- 2022 年 7 月 29 日
- 筆記
正文從這開始~
類型守衛
使用類型守衛來解決React中useRef鉤子「Object is possibly null」的錯誤。比如說,if (inputRef.current) {} 。一旦null被排除在ref的類型之外,我們就能夠訪問ref上的屬性。
下面是一個錯誤如何發生的示例。
import {useEffect, useRef} from 'react';
export default function App() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// ⛔️ Object is possibly 'null'.ts(2531)
inputRef.current.focus();
}, []);
return (
<div>
<input ref={inputRef} type="text" id="message" />
<button>Click</button>
</div>
);
}
程式碼片段中的問題是,TypeScript不能確保我們將一個元素或者一個值賦值給ref,所以它的current屬性可能為null。
為了解決這個錯誤,在訪問ref類型上的屬性之前,我們必須使用類型守衛來從其類型中排除null。
import {useEffect, useRef} from 'react';
export default function App() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// 👉️ ref could be null here
if (inputRef.current != null) {
// 👉️ TypeScript knows that ref is not null here
inputRef.current.focus();
}
}, []);
return (
<div>
<input ref={inputRef} type="text" id="message" />
<button>Click</button>
</div>
);
}
我們使用簡單的if語句作為類型守衛,來確保ref上的current屬性不存儲null。當程式進入到if程式碼塊中,TypeScript就會知道ref對象上的current屬性就不會存儲null。
確保在useRef鉤子上使用泛型,正確的類型聲明
ref上的current屬性。
注意,我們傳遞了一個泛型來將ref的值類型聲明為HTMLInputElement。
一些常用的類型有:HTMLInputElement, HTMLButtonElement, HTMLAnchorElement, HTMLImageElement, HTMLTextAreaElement, HTMLSelectElement 等等。
如果你在ref中存儲了不同的值,請確保將特定類型傳遞給useRef鉤子的泛型,例如const ref = useRef<{name: string}>(null); 。
如果ref上的current屬性存儲了null,我們也可以使用可選鏈?. 操作符進行短路運算。
import {useEffect, useRef} from 'react';
export default function App() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// 👇️ optional chaining (?.)
inputRef.current?.focus();
}, []);
return (
<div>
<input ref={inputRef} type="text" id="message" />
{/* Cannot find name 'button'.ts(2304) */}
<button>Click</button>
</div>
);
}
如果引用是空值(null或者undefined),可選鏈?.操作符會進行短路運算,而不會拋出錯誤。換句話說,如果ref上的current屬性存儲了null,操作符會短路運算從而返回undefined。而不會在undefined上嘗試調用focus方法,導致一個運行時錯誤。
非空斷言
另一種解決方案是使用非空斷言!操作符。
import {useEffect, useRef} from 'react';
export default function App() {
const inputRef = useRef<HTMLInputElement>(null);
useEffect(() => {
// 👇️ using non-null (!) assertion
inputRef.current!.focus();
}, []);
return (
<div>
<input ref={inputRef} type="text" id="message" />
{/* Cannot find name 'button'.ts(2304) */}
<button>Click</button>
</div>
);
}
在TypeScript中,感嘆號標記被稱為非空斷言操作符。被用來從類型中移除null和undefined ,而不用進行任何顯式的類型檢查。
當我們使用非空斷言時,基本上我們就是在告訴TS,ref對象上的current屬性不會存儲null或者undefined。
請注意,這種方法不是類型安全的,因為TypeScript不執行任何檢查以確保屬性不是空的。
總結
造成 "Object is possibly null"的錯誤是因為useRef()鉤子可以傳遞一個初始值作為參數,而我們傳遞null作為初始值。該鉤子返回一個可變的ref對象,其.current屬性被初始化為所傳遞的參數。
當傳遞ref prop給一個元素時,比如<input ref={myRef} /> ,React將ref對象的.current屬性設置為相應的DOM節點,但TypeScript無法確定我們是否會將ref設置為DOM元素,或在我們的程式碼中稍後設置其值。

