この記事の内容
Reactは基本操作しか知らない人が、forwardRef
とuseImperativeHandle
が出てくるコードを修正した時にわからないことを調べた記事です。
主な参考文献
公式ドキュメント。本記事のコードはここにあったものを引用し、状況に応じて多少アレンジしています。
この記事も丁寧で助かった
refオブジェクト
コンポーネントがマウントされたときからアンマウントされるときまで存在し続ける、書き換え可能なオブジェクトのこと。useRef()
を使って作成。(ついでに、renderとマウント・アンマウントの違いが曖昧だったので調べました。)
↓こんなふうに作るよ
const refContainer = useRef(initialValue);
↓こんなふうに使うよ
function TextInputWithFocusButton() {
const inputEl = useRef(null); // 1. refオブジェクトを作成
const onButtonClick = () => { //3. inputEl.currentで下記のinput要素にアクセスできる
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" /> {/* 2. ref属性でinputElを指定すると */}
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}
<input ref={inputEl} />
のように、React(ここでは<input />
)に refオブジェクトを渡した場合、ReactはDOMノードに変更があるたびに .current プロパティをその DOM ノードに設定する。
useRef
は値を中に保持しておくことができる。
-> 関数コンポーネントを再レンダーした際に、前回のレンダー時のデータを取得することが可能
forwardRef
React.forwardRefはref
オブジェクトを配下のツリーの別のコンポーネントに受け渡す React コンポーネントを作成します。
↓こんなふうに使うよ
const FancyButton = React.forwardRef((props, ref) => ( // 2. FancyButtonが第二引数のrefに入っている
<button ref={ref} className="FancyButton">
{props.children} //3. "Click me!"の文字の入った buttonができる
</button>
));
const App = () => {
const ref = React.createRef();
return(
<FancyButton ref={ref}>Click me!</FancyButton>; // 1. FancyButtonコンポーネントを呼び出すと
)
};
この機能を利用すると、親コンポーネント(ここではApp
)側から子コンポーネント内の要素を操作することができる。
useImperativeHandle
useImperativeHandle は ref が使われた時に親コンポーネントに渡されるインスタンス値をカスタマイズするのに使います。
↓こんなふうに使うよ
const FancyInput = (props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => { // `focus()`メソッドを作っている
inputRef.current.focus();
}
}));
return <input ref={inputRef} type="text" ... />;
}
const WrapperFancyInput = React.forwardRef(FancyInput);
const App = () => {
const inputRef = useRef();
const onClick = () => {
inputRef.current.focus(); // FancyInput内のinputにフォーカス
};
return (
<div>
<WrapperFancyInput ref={inputRef} />
<button type="button" onClick={onClick}>
click
</button>
</div>
);
}
この例では、<WrapperFancyInput ref={inputRef} />
をレンダーする親コンポーネント(ここではApp
)は inputRef.current.focus() (←FancyInput
内に記したメソッド)を呼べるようになります。
感想
複雑なコンポーネントの実装中。ここで調べたことが役に立つと良いなーと思いつつ、簡単に終わります。