2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Reactでdocument.getElementByIdを使うと、複数配置した時に壊れる理由

2
Posted at

Reactのコンポーネントの中で、DOMの要素を直接書き換えたいとき、document.getElementByIdを使ったコードを見かけることがあります。単体では動いてしまうため気づきにくいのですが、同じコンポーネントを2つ以上並べた瞬間に壊れます。

Before: document.getElementById で直接操作する

function Timer() {
  useEffect(() => {
    const el = document.getElementById("display");
    if (el) el.textContent = "計測中...";
  }, []);

  return <div id="display"></div>;
}

function App() {
  return (
    <>
      <Timer />
      <Timer />
    </>
  );
}

<Timer />を1つだけ使っているうちは問題なく動きます。ところがAppのように2つ並べると、片方の<div>だけが「計測中...」になり、もう片方は空のままになります。

なぜ2つ目が更新されないのか

document.getElementById("display")は、ページ全体の中で、指定したIDに一致する最初の要素だけを返す仕様です。<Timer />を2つ描画すると、id="display"を持つ<div>もページ内に2つ存在することになりますが、そもそもHTMLの仕様として同じIDを複数の要素に付けるのは想定されていません。それぞれのTimerインスタンスが実行するuseEffectは、どちらも「ページ全体で最初に見つかったid="display"」を掴みに行くため、結果的に同じ1つの要素(多くの場合、最初に描画された方)だけが書き換えられ続けます。

After: useRef で「自分のインスタンスの要素」だけを掴む

function Timer() {
  const displayRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (displayRef.current) displayRef.current.textContent = "計測中...";
  }, []);

  return <div ref={displayRef}></div>;
}

useRefで作ったdisplayRef<div ref={displayRef}>のように渡すと、displayRef.currentにはそのTimerインスタンス自身が描画した<div>だけが入ります。IDのような「ページ全体で共有される目印」ではなく、「このコンポーネントのこの要素」というインスタンスごとの参照になるため、<Timer />をいくつ並べても、それぞれが自分自身の<div>だけを正しく書き換えられます。

単体では気づけない理由

この手のコードが本番に残ってしまう理由として考えられるのは、開発中は1つしか置いていないため、バグが表面化しないことです。動作確認をしている段階では正しく動いて見えるので、コードレビューで「なぜgetElementByIdを使っているのか」を確認しない限り、同じコンポーネントを2つ目に増やすタイミングまで問題に気づけません。

まとめ

  • document.getElementByIdはページ全体で最初に見つかった要素だけを返すため、同じコンポーネントを複数配置すると意図しない要素を書き換えてしまう
  • Reactで特定のDOM要素を直接操作したい場合は、useRefでそのインスタンス専用の参照を作る
  • AIが生成したコードにIDベースのDOM操作(getElementByIdquerySelector)が混ざっていたら、そのコンポーネントが複数配置される可能性があるかどうかを確認するようにしています

未経験者向けの講座を運営しています

未経験から Next.js + Supabase + Claude Code で Webアプリを公開するまで を、全24セッションで体系化した教材です。Claude Code を学習パートナーにする CLAUDE.md と学習モード(learner / developer)の設計までセットで含みます。

※ 中上級者には易しすぎる内容なので、初心者の知り合いへの紹介や社内研修の参考としてどうぞ。

2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?