今日は、useImperativeHandle
を使いつつ学んでいきます。
公式:useImperativeHandle
前提
forwardRefについて知っておく必要がりますので、まずはこちらを読んでおいてくださいね!
公式:forwardRef
- ざっくりいうと...。親は子にrefを渡す、子はforwardRefしておくことで、子の要素を親で触れるようにできるよというものデス。
useImperativeHandleって何?
親コンポーネントが子コンポーネントの内部メソッドを呼び出せるようにするフックです。!
これにより、コンポーネントの内部実装を隠しつつ、親に特定の機能だけを公開できます。
親は必要な操作だけを行え、子の詳細が変更されても影響を受けません!
基本の使い方
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
// 子コンポーネント
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
setFocus: () => {
inputRef.current.focus();
}
}));
return <input ref={inputRef} {...props} />;
});
// 親コンポーネント
const Form = () => {
const inputRef = useRef(null);
const handleClick = () => {
inputRef.current.setFocus();
};
return (
<div>
<CustomInput ref={inputRef} />
<button onClick={handleClick}>inputにフォーカスする</button>
</div>
);
}
親コンポーネントからrefを渡す
<CustomInput ref={inputRef} />
親側で定義した const inputRef = useRef(null);
を、CustomInputに渡します。
子コンポーネントでuseImperativeHandleを使用
useImperativeHandle(ref, () => ({
setFocus: () => {
inputRef.current.focus();
}
}));
ここでrefは親から渡されたものであり、そのrefに対してsetFocusメソッドを定義しています。
このsetFocusメソッドは、親から呼び出すことができます。
もうちょっとメソッドを増やしてみます
import React, { useRef, useImperativeHandle, forwardRef } from 'react';
// 子コンポーネント
const CustomInput = forwardRef((props, ref) => {
const inputRef = useRef(null);
useImperativeHandle(ref, () => ({
setFocus: () => {
inputRef.current.focus();
},
clearInput: () => {
inputRef.current.value = '';
},
highlightText: () => {
inputRef.current.select();
}
}));
return <input ref={inputRef} {...props} />;
});
// 親コンポーネント
const Form = () => {
const inputRef = useRef(null);
const handleFocus = () => {
inputRef.current.setFocus();
};
const handleClear = () => {
inputRef.current.clearInput();
};
const handleSelectText = () => {
inputRef.current.highlightText();
};
return (
<div>
<CustomInput ref={inputRef} />
<button onClick={handleFocus}>フォーカス</button>
<button onClick={handleClear}>クリア</button>
<button onClick={handleSelectText}>テキスト選択</button>
</div>
);
}
子がuseImperativeHandle
して公開したメソッドを、refを通じて親が利用していることがわかりますね!
依存配列
useImperativeHandle
はuseEffect
のように依存配列を持てます。
const [value, setValue] = useState('');
useImperativeHandle(ref, () => ({
setFocus: () => {
inputRef.current.focus();
},
clearInput: () => {
inputRef.current.value = '';
setValue('');
},
highlightText: () => {
inputRef.current.select();
}
}), [value]); // 依存配列として`value`を指定
useImperativeHandleに依存配列を入れたとき
- 依存関係が変化したときのみハンドルを再生成し、パフォーマンスを最適化
useImperativeHandleに依存配列を入れなかったとき
- コンポーネントが再レンダリングされる度に
useImperativeHandle
の関数が再実行され、公開されるインペラティブハンドルが毎回再生成されます。-
インペラティブハンドル?
: 親が子の内部メソッドやプロパティにアクセスできる仕組み。
-
- パフォーマンスに悪影響を与える可能性があります!
useImperativeHandleを使わなくてもよい
import React, { useRef } from 'react';
function useCustomInput() {
const inputRef = useRef(null);
const setFocus = () => {
inputRef.current.focus();
};
const clearInput = () => {
inputRef.current.value = '';
};
return {
inputRef,
setFocus,
clearInput,
};
}
// 子コンポーネント
const CustomInput = ({ inputRef, ...props }) => (
<input ref={inputRef} {...props} />
);
// 親コンポーネント
const Form = () => {
const { inputRef, setFocus, clearInput } = useCustomInput();
return (
<div>
<CustomInput inputRef={inputRef} />
<button onClick={setFocus}>フォーカス</button>
<button onClick={clearInput}>クリア</button>
</div>
);
}
まとめ
今日はuseImperativeHandle
の使い方について学びました!
このフックを使うことで、親が子の内部メソッドを呼び出せるようになり、再利用性や柔軟性が向上しますね!
場合によっては、useImperativeHandle
を使わなくても、カスタムフックやヘルパー関数を使って同様の機能を実現できます。
これらの方法を使い分けて、Reactコンポーネントの設計をより柔軟かつ効率的に行いましょう。