[Basic] useRef Hook Documentation.
rajivpunjabi opened this issue · 11 comments
What cheatsheet is this about? (if applicable)
Basic cheatsheet
What's your issue or idea?
According to the documentation useRef Hook has two options.
const ref1 = useRef<HTMLElement>(null!);
const ref2 = useRef<HTMLElement | null>(null);
The first option will make ref1.current
read-only. But after trying out some examples I feel there are some issues. As it shows MutableRefObject for the first option. So ref1.current
is not read-only.
I am exploring TypeScript with React so maybe I am missing something. Can someone please help me with this?
(If applicable) Reproduction of issue in TypeScript Playground
very nice issue! clearly we are wrong in our useRef notes.
i think @ferdaber might have some insight as to why MutableRefObject
is inferred in that example. however based on this I am inclined to change our documented example to the one in your playground const ref2 = React.useRef<HTMLSpanElement>(null);
According to useRef Hook Type Definitions.
function useRef<T>(initialValue: T): MutableRefObject<T>;
function useRef<T>(initialValue: T|null): RefObject<T>;
function useRef<T = undefined>(): MutableRefObject<T | undefined>;
From Playground.
const ref1 = React.useRef<HTMLSpanElement>(null!); //MutableRefObject
const ref2 = React.useRef<HTMLSpanElement>(null); //RefObject
const ref3 = React.useRef<HTMLSpanElement | null>(null); //MutableRefObject
These ref matches the first two type definitions.
- For ref1 we are defining the initialValue as not null ( non-null assertion operator ) so it matches the first type and returns a MutableRefObject.
- For ref2 we are defining the initialValue as null so it matches the second type and returns a RefObject.
- For ref3 we are defining the generic parameter as either of HTMLSpanElement | null and the initialValue is null so it matches the first type and returns a MutableRefObject.
ok so check out this example though: (playground)
export default function MyComponent() {
const ref1 = React.useRef<HTMLDivElement>(null!); // MutableRefObject
const ref2 = React.useRef<HTMLDivElement>(null); // RefObject
React.useEffect(() => {
console.log(ref1.current.innerHTML);
// TypeScript won't require null-check e.g. ref1 && ref1.current
console.log(ref2.current.innerHTML);
// Type error: Object is possibly 'null'.
});
return [
<div ref={ref1}> etc </div>,
<div ref={ref2}> etc </div>
];
}
the goal is to not require null checks because the ref is always bound to the element in this case. i think this is why i made the original recommendation.
If the goal is not to require null checks then its absolutely correct but stating ref1.current will be read-only is wrong. As it is a MutableRefObject so it is not read-only.
alright :)
i made those tweaks here: https://github.com/typescript-cheatsheets/react/pull/389/files , lmk if any changes but otherwise thank you for the feedback :)
would this be more explanatory?
import * as React from "react";
export default function MyComponent() {
const ref1 = React.useRef<HTMLDivElement>(null!); // MutableRefObject
const ref2 = React.useRef<HTMLDivElement>(null); // RefObject
React.useEffect(() => {
ref1.current=ref2.current!;
// ok
ref2.current=ref1.current;
//Cannot assign to 'current' because it is a read-only property.(2540)
console.log(ref1.current.innerHTML);
// TypeScript won't require null-check e.g. ref1 && ref1.current
console.log(ref2.current.innerHTML);
// Type error: Object is possibly 'null'.
});
return [
<div ref={ref1}> etc </div>,
<div ref={ref2}> etc </div>
];
}
@bochen2014 i dont feel strongly about this code sample 🤷🏽
@sw-yx I think we should not encourage the null check bypassing here. The reason is that check is valid in real life, because the developer may forget to assign the ref to an element in his render, or if the ref-ed element is conditionally rendered:
const Foo = () => {
const ref1 = useRef<HTMLDivElement>(null); // RefObject
useEffect(() => {
console.log(ref1.current); // This will be null
});
return <div> etc </div>; // Oops, I forgot to assign the ref
}
const Foo = () => {
const ref1 = useRef<HTMLDivElement>(null); // RefObject
useEffect(() => {
console.log(ref1.current); // Only works on Monday!
});
return isMonday ? <div ref={ref1} /> : <div> etc </div>;
}
@sw-yx would you mind if I have a PR to clarify this more?