Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between passing in null and undefined in useRef

What's the difference between useRef(null) and useRef(undefined) (or just useRef())?

In TypeScript, the results end up with different types:

const undefinedRef: React.MutableRefObject<undefined> = useRef(undefined)
conse noArgRef: React.MutableRefObject<undefined> = useRef()

const nullRef: React.MutableRefObject<null> = useRef(null)

This also has further consequences, when passing the ref to another element:

const nullRef = useRef(null)
<div ref={nullRef} /> // this works

const undefinedRef = useRef()
<div ref={undefinedRef} /> // compiler failure!
/*
Type 'MutableRefObject<undefined>' is not assignable to type 'string | ((instance: HTMLDivElement | null) => void) | RefObject<HTMLDivElement> | null | undefined'.
  Type 'MutableRefObject<undefined>' is not assignable to type 'RefObject<HTMLDivElement>'.
    Types of property 'current' are incompatible.
      Type 'undefined' is not assignable to type 'HTMLDivElement | null'.ts(2322)
*/

Despite the compiler failure, it still works for my use case (using the useClickAway hook from react-use)

What's the impact at this, looking beyond the TypeScript types?

Here's a CodeSandbox that recreates the failure: https://codesandbox.io/s/prod-resonance-j9yuu?file=/src/App.tsx

like image 274
torkel Avatar asked May 06 '20 12:05

torkel


2 Answers

There isn't any difference between

const undefinedRef: React.MutableRefObject<undefined> = useRef(undefined)
conse noArgRef: React.MutableRefObject<undefined> = useRef()

both of them have undefinedRef.current and noArgRef.current as undefined.

However

const nullRef: React.MutableRefObject<null> = useRef(null)

will have nullRef.current assigned as null

The only impact this will have in your code is when you actually try to access a property from within current or try to check the typeof ref

For instance typeof nullRef.current will be object

and an if condition like

if(typeof nullRef.current === "object") {
   // This will get executed only for nullRef and not for the other two refs
}
like image 58
Shubham Khatri Avatar answered Nov 15 '22 08:11

Shubham Khatri


I believe something like const ref = useRef<SomeType>(null) will make the reference readonly.

See this note from TypeScript cheatsheet.

there are overloads for useRef such that if you use a type param T vs T | null the resultant object is different:

refObjectToPassIntoRefProp = useRef<HTMLDivElement>(null) // @type = {
current: HTMLDivElement | null } const refObjectYouMaintain =
useRef<HTMLDivElement | null>(null) // @type = { current:
HTMLDivElement | undefined } const refObjectYouMaintainToo =
useRef<HTMLDivElement>()

With the first, the current property of the object is read-only to you, the consumer. It's meant to be passed into React as the pointer is maintained by React. In practice, you can still mutate the current property of something you pass into React but it's not recommended as React is supposed to be the owner.

like image 4
zero298 Avatar answered Nov 15 '22 08:11

zero298