教材
ShinCodeさんの
【完全保存版】React Hooksを完全に理解するHooksマスター講座【React18~19対応】
を元に、私が学んだ内容をまとめていきますわ。
お嬢様的Hooks解説
useRef
const ref = useRef(initialValue)
useRef
は、レンダー時に不要な値を参照するためのReactフックですの。useRef
を使用することで、値を保持しつつもコンポーネントの再レンダリングを防ぐことができますの。
useRefの特性
-
値の保持:
useRef
は値を保持するために使用されますの。 -
再レンダリングしない:
useState
と違い、useRef
の値を変更してもコンポーネントは再レンダリングされませんの。
例:useRefの使用方法
以下にuseRef
を使用したコード例を示しますわ。
import { useRef } from "react";
const Lesson3_1 = () => {
const ref = useRef(0);
console.log(ref);
function handleClick() {
ref.current = ref.current + 1;
alert(ref.current);
}
return (
<div>
<input type="text" />
<button onClick={handleClick}>Click me!</button>
<p></p>
</div>
);
};
export default Lesson3_1;
説明
-
useRefの初期化:
const ref = useRef(0);
で、ref
という変数に0
を初期値として保持しますの。
-
handleClick関数:
ボタンがクリックされると、handleClick
関数が実行され、ref.current
の値が1
増加しますの。なんで、
ref
じゃなくて、ref.current
を操作するのかというと、ref
オブジェクト自体が参照のコンテナであり、実際の参照されている値やDOMノードはref.current
に格納されているからですの。
-
レンダリング後も値を保持:
ref.current
はレンダリング後も値を保持し続けますの。例えば、input
タグに入力をしても、ref.current
の値は変更されませんの。
inputタグで何か入力し再レンダリングしても、
値は保持されていることが、コンソールでわかると思いますの。
useRefでDOMを取得して画像スクロールを実装してみよう
useRef
を使用して、DOM要素を参照し、その要素に対して操作を行うことができます。今回は、ボタンを押すと対応する画像までスクロールする機能を実装しますわ。
初期コード
以下は、useRef
を使ってul
タグのDOMを取得する基本的なコードですの。
import { useRef } from "react";
const Lesson3_2 = () => {
const listRef = useRef(null);
const scrollToIndex = () => {
console.log(listRef);
};
return (
<div>
<nav>
<button onClick={() => scrollToIndex()}>Cat1</button>
<button onClick={() => scrollToIndex()}>Cat2</button>
<button onClick={() => scrollToIndex()}>Cat3</button>
</nav>
<div style={{ overflowX: "auto", maxWidth: "700px", margin: "auto" }}>
<ul
className="flex items-center justify-between"
style={{ minWidth: "1300px" }} // コンテナより大きいサイズを指定
ref = {listRef}
>
<li>
<img src="https://loremflickr.com/315/240" alt="Cat 1" />
</li>
<li>
<img src="https://loremflickr.com/300/240" alt="Cat 2" />
</li>
<li>
<img src="https://loremflickr.com/280/240" alt="Cat 3" />
</li>
</ul>
</div>
</div>
);
};
export default Lesson3_2;
実装例:ボタンを押すと画像がスクロール
次に、ボタンを押すと指定した画像までスクロールする機能を実装しますわ。useRef
を使ってul
要素を参照し、その子要素である画像にスクロールしますの。
import { RefObject, useRef } from "react";
const Lesson3_2 = () => {
const listRef: RefObject<HTMLUListElement> = useRef<HTMLUListElement>(null);
const scrollToIndex = (index: number) => {
console.log(listRef);
const listNode = listRef.current;
const imgNode = listNode?.querySelectorAll("li > img")[index];
console.log(imgNode);
imgNode?.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "center"
})
};
return (
<div>
<nav>
<button onClick={() => scrollToIndex(0)}>Cat1</button>
<button onClick={() => scrollToIndex(1)}>Cat2</button>
<button onClick={() => scrollToIndex(2)}>Cat3</button>
</nav>
<div style={{ overflowX: "auto", maxWidth: "700px", margin: "auto" }}>
<ul
className="flex items-center justify-between"
style={{ minWidth: "1300px" }} // コンテナより大きいサイズを指定
ref = {listRef}
>
<li>
<img src="https://loremflickr.com/315/240" alt="Cat 1" />
</li>
<li>
<img src="https://loremflickr.com/300/240" alt="Cat 2" />
</li>
<li>
<img src="https://loremflickr.com/280/240" alt="Cat 3" />
</li>
</ul>
</div>
</div>
);
};
export default Lesson3_2;
説明
-
useRefの初期化:
const listRef: RefObject<HTMLUListElement> = useRef<HTMLUListElement>(null);
で、ul
要素を取得しますの。
-
scrollToIndex関数:
scrollToIndex
関数は、リストの特定の画像までスクロールしますの。listRef.current
からul
要素を取得し、その子要素である画像をquerySelectorAll
で取得しますの。取得した画像要素に対して
scrollIntoView
メソッドを使用し、スムーズにスクロールしますの。
-
DOM要素へのアクセス:
ref
属性を使用してul
タグをlistRef
に割り当てますの。これにより、scrollToIndex
関数内でlistRef.current
を介してul
要素にアクセスできますの。
UseRef()を使って余計な再レンダリングを防ぐ方法
useRef
を使用することで、コンポーネントの不要な再レンダリングを防ぐことが出来ますの。これにより、パフォーマンスを向上させることができますわ。
状態管理による再レンダリング
以下のコードでは、useState
を使用して、input
を管理している為、onChange
イベントが発生されるたびに再レンダリングされますわ。
import { useState } from "react";
const Lesson3_3 = () => {
const [inputText, setInputText] = useState("");
console.log("render")
for(let i; i < 50; i++) {
console.log("aiueo");
}
const handleClick = () => {
alert(inputText);
};
return (
<div>
<input type="text" className="border-b" value={inputText} onChange={(e) => setInputText(e.target.value)}/>
<button onClick={handleClick}>input入力値を見る</button>
</div>
);
};
export default Lesson3_3;
useRefを使用して再レンダリングを防ぐ
以下のコードでは、useRef
を使用してinput
の値を管理し、再レンダリングを防いでいますの。
import { useRef, useState } from "react";
const Lesson3_3 = () => {
// const [inputText, setInputText] = useState("");
const inputRef = useRef<HTMLInputElement>(null);
console.log("render")
const handleClick = () => {
alert(inputRef.current?.value)
};
return (
<div>
<input type="text" className="border-b" ref={inputRef} />
<button onClick={handleClick}>input入力値を見る</button>
</div>
);
};
export default Lesson3_3;
説明
-
useRefの初期化:
const inputRef = useRef<HTMLInputElement>(null);
でinput
の値を参照しますの。
-
再レンダリングの防止:
useState
を使用せずに、useRef
を使うことで、input
の値の変更による再レンダリングを防ぎますの。onChange
イベントが発生しても、コンポーネントは再レンダリングされませんの。
-
inputの値の取得:
inputRef.current?.value
を使用してinput
の値を取得しますの。handleClick
関数内で、ボタンがクリックされたときにinput
の値をアラートで表示しますの。
実際の使用例
useRef
は、以下のような場合に非常に便利ですわ。
-
パフォーマンスの最適化:
- 重い処理が含まれる場合、再レンダリングを防ぐことでパフォーマンスを最適化しますの。
-
DOM要素への直接アクセス:
-
input
要素の値を直接参照する場合など、DOM要素に対して直接操作を行いたい場合に使用しますの。
-
-
ECサイトでの検索機能:
- 商品検索の入力フィールドなど、頻繁に更新されるが再レンダリングを避けたい場合に有効ですわ。
forwardRef()で別のコンポーネントのDOMノードにアクセスする
forwardRef
を使用すると、親コンポーネントから子コンポーネントにref
を渡すことができ、子コンポーネントのDOMノードに直接アクセスできますの。以下に、その実装例を示しますわ。
親コンポーネントの実装
親コンポーネントであるLesson3_4
から子コンポーネントMyVideoPlayer
にref
を渡し、動画の再生と一時停止を制御しますの。
import { useRef } from "react";
import { MyVideoPlayer } from "./MyVideoPlayer";
const Lesson3_4 = () => {
const videoRef = useRef<HTMLVideoElement>(null);
return (
<div>
<button onClick={() => videoRef.current?.play()}>Play</button>
<button onClick={() => videoRef.current?.pause()}>Pause</button>
<br />
<MyVideoPlayer
ref={videoRef}
src="https://interactive-examples.mdn.mozilla.net/media/cc0-videos/flower.mp4"
type="video/mp4"
width="250"
/>
</div>
);
};
export default Lesson3_4;
子コンポーネントの実装
子コンポーネントMyVideoPlayer
では、forwardRef
を使用して親コンポーネントから渡されたref
を受け取りますの。
import { forwardRef } from "react";
type MyVideoPlayerProps = {
width: string;
type: string;
src: string;
};
export const MyVideoPlayer = forwardRef<HTMLVideoElement, MyVideoPlayerProps>(
(props, ref) => {
return (
<video width={props.width} ref={ref}>
<source src={props.src} type={props.type} />
</video>
);
}
);
説明
-
親コンポーネントの設定:
useRef
を使用してvideoRef
を作成し、MyVideoPlayer
コンポーネントにref
として渡しますの。ボタンのクリックイベントで
videoRef.current?.play()
およびvideoRef.current?.pause()
を呼び出して、動画の再生と一時停止を制御しますの。
-
子コンポーネントの設定:
forwardRef
を使用して、子コンポーネントMyVideoPlayer
が親コンポーネントから渡されたref
を受け取りますの。ref
を<video>
要素に渡すことで、親コンポーネントから直接<video>
要素にアクセスできるようにしますの。
まとめ
useRefの基本特性:
useRef
はレンダリング時に不要な値を保持するためのReactフックですの。useState
とは異なり、値の変更がコンポーネントの再レンダリングを引き起こさないため、パフォーマンスの最適化に有効ですの。
DOM操作の実装例:
useRef
を使ってDOM要素にアクセスし、その要素に対する操作を行うことができますの。具体例として、ボタンを押すと対応する画像までスムーズにスクロールする機能の実装がありますの。
再レンダリング防止:
useState
を使う場合と比べて、input
の値の変更による再レンダリングを防ぎ、パフォーマンスを向上させることができますの。
実際の使用例:
useRef
はパフォーマンスの最適化やDOM要素への直接アクセスが必要な場合に特に有用ですわ。例えば、ECサイトの検索機能など、頻繁に更新されるが再レンダリングを避けたい場面で効果を発揮しますの。
forwardRefの活用:
forwardRef
を使用することで、親コンポーネントから子コンポーネントにref
を渡し、子コンポーネントのDOMノードに直接アクセスすることが可能ですの。これにより、例えば動画の再生や一時停止を親コンポーネントから制御することができますわ。