Chapter 3 of 4

Typing Events and Refs

Getting handler and DOM types right without fighting the compiler.

React ships types for every synthetic event. The generic parameter is the element the handler is attached to.

function Form() {
  function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
    console.log(event.target.value);       // typed as string
  }

  function handleSubmit(event: React.FormEvent<HTMLFormElement>) {
    event.preventDefault();
  }

  function handleClick(event: React.MouseEvent<HTMLButtonElement>) {
    console.log(event.currentTarget.name);
  }

  function handleKeyDown(event: React.KeyboardEvent<HTMLInputElement>) {
    if (event.key === "Enter") submit();
  }
}

Let inference do the work

An inline handler does not need annotations at all - React's element types already describe the parameter.

// event is inferred as React.ChangeEvent<HTMLInputElement>
<input onChange={(event) => setValue(event.target.value)} />

target versus currentTarget

  • currentTarget is the element the handler is on, and is typed precisely.
  • target is whatever was actually clicked, so it is typed as the more general EventTarget.

Common DOM element types

HTMLInputElement, HTMLTextAreaElement, HTMLSelectElement
HTMLButtonElement, HTMLFormElement, HTMLAnchorElement
HTMLDivElement, HTMLCanvasElement, HTMLVideoElement

Forwarding a ref

forwardRef takes the ref type first, then the props type.
interface InputProps extends React.ComponentProps<"input"> {
  label: string;
}

const TextField = forwardRef<HTMLInputElement, InputProps>(
  function TextField({ label, ...rest }, ref) {
    return (
      <label>
        {label}
        <input ref={ref} {...rest} />
      </label>
    );
  }
);