😽

useMergeRef hookとは

2021/05/21に公開

UIライブラリのコードを読んでいるとuseMergeRef hookというのがよく定義されている。

最初はこれが何なのか分からなかったが、refを複数指定するための仕組みだった。

すなわちReact.forwardRefで外側からrefを受け取りつつも、コンポーネント内部でもrefによる操作をしたい。そんな要件を満たすためのものだった。

const Example = React.forwardRef((props, ref) => {
  const internalRef = React.useRef()
  const refs = useMergeRefs(internalRef, ref)

  return (
    <div {...props} ref={refs}>
      A div with multiple refs.
    </div>
  )
})

実装は至ってシンプル。

import * as React from "react"

type ReactRef<T> = React.Ref<T> | React.MutableRefObject<T>

export function assignRef<T = any>(ref: ReactRef<T> | undefined, value: T) {
  if (ref == null) return

  if (typeof ref === "function") {
    ref(value)
    return
  }

  try {
    ref.current = value
  } catch (error) {
    throw new Error(`Cannot assign value '${value}' to ref '${ref}'`)
  }
}


export function useMergeRefs<T>(...refs: (ReactRef<T> | undefined)[]) {
  return React.useMemo(() => {
    if (refs.every((ref) => ref == null)) {
      return null
    }
    return (node: T) => {
      refs.forEach((ref) => {
        if (ref) assignRef(ref, node)
      })
    }
  }, refs)
}

refにはuseRef()で生成したrefオブジェクトとコールバックがあるので、そのへんを考慮している。

複数のrefをまとめた一つのrefを作ると直感的に解釈できなかったのは不甲斐ない。

Discussion