HextaUI

useComposedRef

Conserve une ref vers votre propre élément tout en la transmettant à la ref passée par le parent.

"use client"

import * as React from "react"

import { Button } from "@/components/ui/button"
import { useComposedRef } from "@/hooks/use-composed-ref"

function CharacterInput({ ref, ...props }: React.ComponentProps<"input">) {
  const [inputRef, setRef] = useComposedRef<HTMLInputElement>(ref)
  const [length, setLength] = React.useState(0)

  return (
    <div className="flex h-9 w-full items-center gap-2 rounded-md bg-muted pe-3 focus-within:ring-3 focus-within:ring-focus-ring">
      <input
        ref={setRef}
        {...props}
        onChange={(event) => {
          props.onChange?.(event)
          setLength(event.target.value.length)
        }}
        className="h-full min-w-0 flex-1 bg-transparent ps-3 text-sm outline-none focus-visible:outline-hidden pointer-coarse:text-touch"
      />
      <button
        type="button"
        hidden={length === 0}
        onClick={() => {
          const input = inputRef.current
          if (input) {
            input.value = ""
            setLength(0)
            input.focus()
          }
        }}
        className="text-xs text-muted-foreground hover:text-foreground"
      >
        Clear
      </button>
    </div>
  )
}

export function UseComposedRefDemo() {
  const ref = React.useRef<HTMLInputElement>(null)

  return (
    <div className="flex w-full max-w-xs flex-col gap-3">
      <CharacterInput ref={ref} aria-label="Search" placeholder="Search" />
      <Button variant="outline" size="sm" onClick={() => ref.current?.focus()}>
        Focus from the parent
      </Button>
    </div>
  )
}
pnpm dlx shadcn@latest add https://hextaui.com/r/use-composed-ref.json

Ajoute le hook et tout ce dont il dépend à votre projet.

import { useComposedRef } from "@/hooks/use-composed-ref"
function SearchInput({ ref, ...props }: React.ComponentProps<"input">) {
  const [inputRef, setRef] = useComposedRef<HTMLInputElement>(ref)

  React.useEffect(() => {
    inputRef.current?.select()
  }, [inputRef])

  return <input ref={setRef} {...props} />
}

Dans React 19, ref est une prop ordinaire. Quand votre composant a aussi besoin de l'élément pour lui-même, vous avez deux refs et un seul attribut ref. Ce hook vous donne une ref objet à lire, plus un callback qui remplit les deux refs.

function SearchInput({ ref, ...props }: React.ComponentProps<"input">) {
  const inputRef = React.useRef<HTMLInputElement>(null)
  return <input ref={inputRef} {...props} />
}

Cela compile, mais le ref du parent est perdu, et ref.current reste donc null chez le parent. Avec useComposedRef, le parent et le composant reçoivent tous deux l'élément.

  • Le inputRef renvoyé est un RefObject normal : vous pouvez donc le passer à des hooks qui en attendent un, comme useAutosize ou useInvalidShake.
  • setRef garde la même identité tant que la ref du parent la garde, pour que React ne la détache ni ne la rattache à chaque rendu.
  • Fonctionne avec des refs objet, des refs callback, ou sans ref du tout.

Utilisez useComposedRef quand vous combinez exactement une ref transmise avec votre propre ref objet, ce qui est le cas de la plupart des composants. Utilisez useMergedRef quand vous en combinez plus de deux, quand l'une est une ref callback issue d'un autre hook, ou quand une ref callback transmise renvoie une fonction de nettoyage de React 19. useComposedRef appelle une ref callback avec null au détachement au lieu d'exécuter son nettoyage.

PropTypePar défaut
refLa ref reçue par votre composant.
Ref<T> | undefined–
Valeur de retourDescription
[0] RefObject<T | null>Votre propre ref vers l'élément.
[1] (node: T | null) => voidÀ passer à la ref de l'élément. Remplit les deux refs.

Input, Textarea, NativeSelect, Field et InputGroup.