useRef
と同じ様な使用用途です。useRef
はコンポーネント内の HTML 要素を触る為のフックでしたが、useImperativeHandle
は親コンポーネントにコンポーネント内の要素を触る為の手段を提供する為のフックです。React.forwardRef
との違いは2つほどあると思います。
コンポーネント内の要素を親コンポーネントから直接触られない
コンポーネント内の複数の要素(参照)を親コンポーネントは触ることができる
例。このフックは親から参照をもらうためにforwardRef
と共に使います。
const Checkbox = forwardRef((_, ref) => {
const checkboxRef = useRef(null);
useImperativeHandle(ref, () => ({
check() {
if (checkboxRef.current !== null) {
checkboxRef.current.checked = true;
}
}
}));
return ;
});
こうして渡ってきた親コンポーネントからのref.current
に対して、useImperativeHandle
ハンドラーを生やします。この例ではcheck
メソッドを持つオブジェクトを返していますが、これがまさにref.current.check
を実装していることになります。
この中のcheckboxRef
は要素の為のuseRef
なので普段通りに使えます。
親コンポーネントからref.current.check()
が呼ばれるとコンポーネント内のinput[type=checkbox]
のチェックフラグをtrue
に書き換えます。(React にはもっと優れた方法があるので実際にはこのように実装しないように)
親コンポーネント
親です。親では何度も言っているref.current.check()
の実行の仕組みとハンドラーを生やすためのref
をforwardRef
なコンポーネントに渡す必要があります。
export default () => {
const ref = React.useRef(null);
const onClick = React.useCallback(() => {
ref.current.check();
}, []);
return (
);
};
forwardRef
に渡すuseRef
も基本的にnull
でフォールバック値を与えます。これは子要素が存在しない場合はref.current
にはnull
が入るようにする為です。
check!
ボタンを押してinput[type=checkbox]
にチェックが入るか確認できたら完了です。
ユースケース
Pulldown 実装
この要素は、Pulldown にフォーカスが入った後サジェストが開き、その後サジェストの最後の要素が抜けるまでその状態を維持し、抜けたら Pulldown を閉じるというような動作が期待されます。
サジェストの要素どれかにフォーカスがあたってるかをuseImperativeHandle
を使って親から調べれるようにすると、親要素である Pulldown の開く・閉じるタイミングが簡単に制御できるようになります。また子の参照だけではなく上の方で書いた Checkbox のように DOM を操作することもできるので、例のように子がそういうハンドラーを提供すれば、親から Focus している要素を変更するといった処理も行えます。