SolvedDefinitelyTyped [@types/react] RefObject.current should no longer be readonly

It is now okay to assign to ref.current, see example:

Trying to assign a value to it gives the error: Cannot assign to 'current' because it is a constant or a read-only property.

47 Answers

✔️Accepted Answer

It's not. It'a intentionally left readonly to ensure correct usage, even if it's not frozen. Refs initialized with null without specifically indicating you want to be able to assign null to it are interpreted as refs you want to be managed by React -- i.e. React "owns" the current and you're just viewing it.

If you want a mutable ref object that starts with a null value, make sure to also give | null to the generic argument. That will make it mutable, because you "own" it and not React.

Perhaps this is easier for me to understand as I have worked with pointer-based languages often before and ownership is very important in them. And that's what refs are, a pointer. .current is dereferencing the pointer.

Other Answers:

By default if you create a ref with a null default value and specify its generic parameter you're signaling your intent to have React "own" the reference. If you want to be able to mutate a ref that you own you should declare it like so:

const intervalRef= useRef<NodeJS.Timeout | null>(null) // <-- the generic type is NodeJS.Timeout | null
// you should always give an argument even if the docs sometimes miss them
// $ExpectError
const ref1 = useRef()
// this is a mutable ref but you can only assign `null` to it
const ref2 = useRef(null)
// this is also a mutable ref but you can only assign `undefined`
const ref3 = useRef(undefined)
// this is a mutable ref of number
const ref4 = useRef(0)
// this is a mutable ref of number | null
const ref5 = useRef<number | null>(null)
// this is a mutable ref with an object
const ref6 = useRef<React.CSSProperties>({})
// this is a mutable ref that can hold an HTMLElement
const ref7 = useRef<HTMLElement | null>(null)
// this is the only case where the ref is immutable
// you did not say in the generic argument you want to be able to write
// null into it, but you gave a null anyway.
// I am taking this as the sign that this ref is intended
// to be used as an element ref (i.e. owned by React, you're only sharing)
const ref8 = useRef<HTMLElement>(null)
// not allowed, because you didn't say you want to write undefined in it
// this is essentially what would happen if we allowed useRef with no arguments
// to make it worse, you can't use it as an element ref, because
// React might write a null into it anyway.
// $ExpectError
const ref9 = useRef<HTMLElement>(undefined)

I was trying to handle React Navigation's Navigating without the navigation prop initialization as well, I ended up manually assigning the type like so since the type is surely going to be either a boolean or null.

export const isReadyRef: React.MutableRefObject<boolean | null> = React.createRef()

For that, you're going to have to cheat.

(ref.current as React.MutableRefObject<T> ).current = element;

Yes, this is a bit unsound, but it's the only case I can think of where this kind of assignment is deliberate and not an accident -- you're replicating an internal behavior of React and thus have to break the rules.

More Issues: