Search code examples
javascriptreactjstypescriptref

React TypeScript select node contents from a ref


Is there a chance I can possibly feed the React ref to the document range .selectNodeContents() function. The error I end up with is:

Argument of type 'HTMLDivElement | null' is not assignable to parameter of type 'Node'.
  Type 'null' is not assignable to type 'Node'.ts(2345)
(property) React.RefObject<HTMLDivElement>.current: HTMLDivElement | null

My assumption is that the ref is declared before it actually was assigned, but I don't really understand how to overcome this in TypeScript. Sorry if duplicate, just didn't find it on the Internet

export const Code: FC<ICode> = ({
    codeString
}) => {
    const codeRef = useRef<HTMLDivElement>(null);

    const clickHandler = (e: React.MouseEvent<HTMLDivElement>) => {
        let range = document.createRange();
        range.selectNodeContents(codeRef.current); // <-- Error here!!
        window.getSelection()?.removeAllRanges();
        window.getSelection()?.addRange(range);

        navigator?.clipboard?.writeText(codeString);
    };

    return (
        <div
          id="Code"
          className="Code"
          onClick={clickHandler}
          ref={codeRef}
        >
            { codeString }
        </div>
    );
}

Solution

  • This is because codeRef.current might be either null or HTMLDivElement whether selectNodeContents expects only Node.

    All you need to do is to wrap codeRef.current into condition:

    import React, { FC, useRef } from 'react'
    
    interface ICode {
      codeString: string
    }
    export const Code: FC<ICode> = ({
      codeString
    }) => {
      const codeRef = useRef<HTMLDivElement>(null);
    
      const clickHandler = (e: React.MouseEvent<HTMLDivElement>) => {
        let range = document.createRange();
        if (codeRef.current) {
          range.selectNodeContents(codeRef.current); // ok
          window.getSelection()?.removeAllRanges();
          window.getSelection()?.addRange(range);
    
          navigator?.clipboard?.writeText(codeString);
        }
      };
    
      return (
        <div
          id="Code"
          className="Code"
          onClick={clickHandler}
          ref={codeRef}
        >
          {codeString}
        </div>
      );
    }
    

    Playground