I have a function which is called on a click event:
useEffect(() => {
document.addEventListener('click', (e) => handleClickOutside(e), true);
});
The function:
const myElement = useRef(null);
const handleClickOutside = (e: MouseEvent) => {
if (!colorPickerRef) {
return;
}
if (!myElement.current?.contains(e.target)) {
// do something
}
};
However I get the error property 'contains' does not exist on type 'never'
How can I promise typescript that myElement.current
is not null?
CodePudding user response:
I think that the better way to handle this is to also pass to the useRef a type.
By this I mean something like this : useRef<HTMLDivElement>
for example
const myElement = useRef<HTMLDivElement>(null)
const handleClickOutside = (e: MouseEvent) => {
if (!myElement.current) {
// If myElement is null then we return
return
}
if (!myElement.current.contains(e.target)) {
// do something
}
}
CodePudding user response:
Problem
The type for useRef
looks like this:
/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
*
* Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
* value around similar to how you’d use instance fields in classes.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
function useRef<T>(initialValue: T): MutableRefObject<T>;
// convenience overload for refs given as a ref prop as they typically start with a null value
/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
*
* Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
* value around similar to how you’d use instance fields in classes.
*
* Usage note: if you need the result of useRef to be directly mutable, include `| null` in the type
* of the generic argument.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
function useRef<T>(initialValue: T|null): RefObject<T>;
// convenience overload for potentially undefined initialValue / call with 0 arguments
// has a default to stop it from defaulting to {} instead
/**
* `useRef` returns a mutable ref object whose `.current` property is initialized to the passed argument
* (`initialValue`). The returned object will persist for the full lifetime of the component.
*
* Note that `useRef()` is useful for more than the `ref` attribute. It’s handy for keeping any mutable
* value around similar to how you’d use instance fields in classes.
*
* @version 16.8.0
* @see https://reactjs.org/docs/hooks-reference.html#useref
*/
function useRef<T = undefined>(): MutableRefObject<T | undefined>;
It either returns a RefObject
or a MutableRefObject
. They essentially have the same shape and look like this:
interface RefObject<T> {
readonly current: T | null;
}
interface MutableRefObject<T> {
current: T;
}
From the above you can see the type of current
takes on the type of the argument useRef
is called with. You called useRef
with null
, therefore T
has a type of null
, therefore current
has a type of null
. The contains
method cannot be called on null
hence the TypeScript error.
Solution
useRef
accepts a type parameter (see generics) which allows you to give a type to T
yourself. You might do something like this (replace HTMLElement
with a type more specific to your case):
import { useRef } from "react";
const myElement = useRef<HTMLElement>(null);
const handleClickOutside = (e: MouseEvent) => {
if (!myElement.current?.contains(e.target)) {
// do something
}
};