20260421tue
ポイント
useCallBackは第二引数が変わると再生成される。
window.addEventLisner = ブラウザに、第一引数に対して、第二引数の反応をするようにセットする。
useEffect の仕様として、コールバックが関数を返すとそれがクリーンアップ関数になると決まっている。 useEffectでやったことを「元に戻す」処理を書く。 useEffect(()=>{ retrun ()=> },[] )
コードの流れ
posts/selectedIndex/router が変わる → useCallback が新しい handleKeyDown を生成 → useEffect が古いリスナーをremove → 新しいリスナーをadd
コード
// キー上下で記事を選択。エンターで記事編集ページに移動。
// 第二引数のいずれかが変更されると再生成される。
const handleKeyDown = useCallback(
(e: KeyboardEvent) => {
if (posts.length === 0) return;
if (e.key === "ArrowDown") {
e.preventDefault();
setSelectedIndex(prev =>
prev === null ? 0 : Math.min(prev + 1, posts.length - 1)
);
} else if (e.key === "ArrowUp") {
e.preventDefault();
setSelectedIndex(prev =>
prev === null ? posts.length - 1 : Math.max(prev - 1, 0)
);
} else if (e.key === "Enter" && selectedIndex !== null) {
router.push(`/admin/posts/${posts[selectedIndex].id}/edit`);
}
},
[posts, selectedIndex, router]
);
// handleKeyDownが再生成されるたびに動く。
useEffect(() => {
// ブラウザにセットする。('keydown'が起きたら, handleKeyDownを設定する。
window.addEventListener("keydown", handleKeyDown);
// クリーンアップ関数。useEffectの処理をもとに戻す。
return () => window.removeEventListener("keydown", handleKeyDown);
}, [handleKeyDown]);