← Hooked.
useImperativeHandle
公式ドキュメント ↗type FancyInputHandle = { focus: () => void; clear: () => void; shake: () => void };
function FancyInput({ ref }: { ref?: React.Ref<FancyInputHandle> }) {
const inputRef = useRef<HTMLInputElement>(null);
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => {
if (inputRef.current) inputRef.current.value = "";
},
shake: () => {
/* 独自のアニメーション処理 */
},
}));
return <input ref={inputRef} />;
}
// 親側
const handleRef = useRef<FancyInputHandle>(null);
<FancyInput ref={handleRef} />;
handleRef.current?.shake();DOM要素をそのまま渡さない理由
// DOM要素をそのまま渡す場合、親は何でもできてしまう
<input ref={parentRef} />;
parentRef.current.value = "直接書き換え"; // Reactのstateを介さない変更
parentRef.current.remove(); // DOMから消してしまうことも可能
parentRef.current.style.color = "red"; // スタイルも自由に触れる
// useImperativeHandleなら、公開する操作を自分で絞り込める
useImperativeHandle(ref, () => ({
focus: () => inputRef.current?.focus(),
clear: () => {
/* Reactの管理下で安全にクリア */
},
}));input要素そのものをrefで親へ渡すこともできるが、それでは親にできることが広がりすぎることがある。子コンポーネントの中身(DOM構造やライブラリの詳細)を隠しつつ、親が本当に必要な操作だけを安全に公開できる。中身を後から作り替えても、公開した操作さえ変えなければ親のコードは壊れない。
注意点: あくまで最後の手段として使う
「親が子に命令する」ような書き方は、Reactの「状態が変わったら画面が描き直される」という基本のスタイルから外れた、いわば非常口(脱出ハッチ)。propsやstateで表現できるなら、そちらを優先した方がコンポーネント同士のつながりが緩くなる。フォーカス制御、スクロール位置の調整、このデモのシェイクのような一時的なアニメーションのトリガーなど、propsやstateでは表現しづらい操作に限って使うのがおすすめ。