简体   繁体   中英

what type to give ref in React/TypeScript while using React.forwardRef?

I just can't get the typings right. I am forwarding a ref which works correctly if I set ref: any as shown below and ref.current.focus() works.

function EditorFeat(): JSX.Element {
  const ref = useRef<HTMLElement | null>(null);

   // ...
   return (
    ......
    <Editor state={state} dispatch={dispatch} ref={ref} />
     ....
)
}

export default EditorFeat;

interface IEditor {
  state: AppState;
  dispatch: React.Dispatch<Action>;
}

// change ref:any to correct typings!
function Editor({ state, dispatch }: IEditor, ref: any): JSX.Element {
// ...
  return <DraftJsEditor ref={ref} /> // import from 'draft-js'
}

export default React.forwardRef(Editor);

but as soon as I try to get ref type right, I keep getting errors!

if I do function Editor({state, dispatch}: IEditor, ref: HTMLElement): JSX.Element

I get next error:

  Overload 1 of 2, '(props: Readonly<DraftEditorProps>): DraftEditor', gave the following error.
    Type 'HTMLElement' is not assignable to type 'string | ((instance: DraftEditor | null) => void) | RefObject<DraftEditor> | null | undefined'.
      Type 'HTMLElement' is not assignable to type 'string'.
  Overload 2 of 2, '(props: DraftEditorProps, context?: any): DraftEditor', gave the following error.
    Type 'HTMLElement' is not assignable to type 'string | ((instance: DraftEditor | null) => void) | RefObject<DraftEditor> | null | undefined'.
      Type 'HTMLElement' is not assignable to type 'string'

if I try React.RefObject<DraftEditor> I get:

Argument of type '({ state, dispatch }: IEditor, ref: RefObject<DraftEditor>) => Element' is not assignable to parameter of type 'ForwardRefRenderFunction<DraftEditor, IEditor>'.
  Types of parameters 'ref' and 'ref' are incompatible.
    Type '((instance: DraftEditor | null) => void) | MutableRefObject<DraftEditor | null> | null' is not assignable to type 'RefObject<DraftEditor>'.
      Type 'null' is not assignable to type 'RefObject<DraftEditor>'

With functional component this works like a charm, even though a little bit unusual:

function RichEditor(props: RichEditorProps) {
  const [editor, setEditor] = React.useState<Editor | null>(null);

  React.useEffect(() => {
    editor?.focus() // or anywhere you want to use it
  }, [editor])
    

  return (
    <Editor
      ref={editor => setEditor(editor)}
    />
  )
}

If you write React.forwardRef directly at the top of the component and not in the line of the return statement, you only have to type React.forwardRef :

interface IEditor {
  state: AppState;
  dispatch: React.Dispatch<Action>;
}

const Editor = React.forwardRef<HTMLInputElement, IEditor>({ state, dispatch }, ref) {
  // ...
  return <DraftJsEditor ref={ref} /> // import from 'draft-js'
}

export default Editor;

Explanation : You type React.forwardRef with two generic type parameters T and P :

React.forwardRef<T, P>(Editor)
  • T is the type of the HTML element you are eventually referencing via DraftJsEditor , so this might be HTMLDivElement or HTMLInputElement or any other element. The TypeScript error message will usually guide you to the right one when first just pick any wrong one.
  • P are the props of the Editor component.

So in your case it could be React.forwardRef<HTMLInputElement, IEditor>(Editor) as you can see in the big solution above.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM