HextaUI

useComposedRef

Mantiene una ref a tu propio elemento y sigue reenviándola a la ref que haya pasado el padre.

"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

Añade el hook y todo lo que necesita a tu proyecto.

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} />
}

En React 19, ref es una prop normal. Cuando tu componente también necesita el elemento para sí mismo, tienes dos refs y un solo atributo ref. Este hook te da un ref de objeto para leer, más un callback que rellena ambos refs.

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

Esto compila, pero el ref del padre se descarta, así que ref.current sigue siendo null en el padre. Con useComposedRef, tanto el padre como el componente reciben el elemento.

  • El inputRef devuelto es un RefObject normal, así que puedes pasarlo a hooks que esperan uno, como useAutosize o useInvalidShake.
  • setRef mantiene la misma identidad mientras el ref del padre la mantiene, así que React no lo desvincula y vuelve a vincular en cada renderizado.
  • Funciona con refs de objeto, refs de callback y sin ref alguno.

Usa useComposedRef cuando combines exactamente un ref reenviado con tu propio ref de objeto, que es el caso de la mayoría de los componentes. Usa useMergedRef cuando combines más de dos, cuando uno de ellos sea un ref de callback de otro hook, o cuando un ref de callback reenviado devuelva una función de limpieza de React 19. useComposedRef llama a un ref de callback con null al desmontar en lugar de ejecutar su limpieza.

PropTipoPredeterminado
refEl ref que recibió tu componente.
Ref<T> | undefined–
DevuelveDescripción
[0] RefObject<T | null>Tu propio ref al elemento.
[1] (node: T | null) => voidPásalo al ref del elemento. Rellena ambos refs.

Input, Textarea, NativeSelect, Field e InputGroup.