jQueryコードを段階的にReactに置き換える方法

## 前提
Altaryの開発初期段階では、開発スピードを優先して jQuery を使って構築していました。

※サービスの詳細は Altary公式サイト（https://altary.io) をご覧ください。


## やりたいこと
サービスの本格運用が見えてきたことで、保守性や拡張性の観点から React を導入したいと考えました。

## 留意点
すでにベータ版が公開されているため、移行にともなって開発が大幅に遅延することや、サービスが破損することは極力避けたい状況でした。


## 段階的な移行の利点

### ① 初期コストを抑えられる
- jQueryを一気にReactへ移行するには、大量のリファクタが必要ですが、段階的に進めれば影響範囲を限定できます。

- チーム全体でReactの学習を並行して進めやすく、負担も少なくて済みます。

- 現在のjQueryコードが問題なく動作しているなら、壊さずに一部だけReact化することで改善可能です。

### ② Reactの利点を必要なところだけ活かせる
- useState や props による状態管理、仮想DOMなどのReactの強みを限定的に活用できます。

- コメント欄やタブUIなど、動的で複雑な部分だけReact化すれば、jQueryよりもシンプルに書けます。

- UIをコンポーネント化できるため、再利用性が高く、別の画面でも使い回しやすくなります。

### ③ スケジュールに合わせて柔軟に進められる
- 「使用頻度の高い画面だけReact化」「時間があるときに少しずつ進める」など、現場の状況に合わせて移行可能です。

- 開発や運用を止めることなく、日々の業務と並行して改善を進められるのも大きな利点です。

### ④ トラブル時にも戻しやすい
- Reactを限定的に導入しているため、問題が起きたらその部分だけjQueryに戻すことも簡単です。

- jQueryとReactが共存できる構成なので、安全かつ段階的に移行できます。

<br>

## 移行前のコード例 （HTML + jQuery）


### HTML
```
<div id="comments"></div>
<button id="send-btn">送信</button>

<script id="comment-template" type="text/template">
  <div class="comment">
    <p class="body"></p>
  </div>
</script>
```

### jQuery
```
const comments = [
  { id: 1, body: "これはコメント1です" },
  { id: 2, body: "これはコメント2です" },
];

function renderComments() {
  const template = $('#comment-template').html();
  $('#comments').empty();

  comments.forEach((comment) => {
    const $comment = $(template);
    $comment.find('.body').text(comment.body);
    $('#comments').append($comment);
  });
}

$(function () {
  renderComments();

  $('#send-btn').on('click', () => {
    alert('コメントを送信します');
  });
});

```

おおまかには、以下のようなフォームをイメージしてください。
（実際には、CSSによるスタイル指定が必要となります）

![ChatGPT Image 2025年4月6日 18_04_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4026790/9557839e-ba8e-423f-a97b-c06059db2f6a.png)



送信するとコメントが追加されます。

<br>

## 移行後のコード例 （HTML+jQuery+React）

### HTML
```
<div id="comment-root"></div>
```

### jquery
```
<script>
  const comments = [
    { id: 1, body: "これはコメント1です" },
    { id: 2, body: "これはコメント2です" }
  ];

  $(function () {
    const el = document.getElementById("comment-root");
    if (el && window.renderComments) {
      window.renderComments(el, comments);
    }
  });
</script>
```

### react
#### ①render.tsx(マウントスクリプト)
```
// src/render.tsx
import React from 'react';
import { createRoot } from 'react-dom/client';
import { CommentList } from './CommentList';

const roots = new Map<HTMLElement, ReturnType<typeof createRoot>>();

(window as any).renderComments = (el: HTMLElement, comments: any[]) => {
  if (!el) return;

  let root = roots.get(el);
  if (!root) {
    root = createRoot(el);
    roots.set(el, root);
  }

  root.render(<CommentList comments={comments} />);
};

```

#### ②CommentList.tsx（コンポーネント）
```
// src/CommentList.tsx
import React from 'react';

type Comment = {
  id: number;
  body: string;
};

type Props = {
  comments: Comment[];
};

export const CommentList: React.FC<Props> = ({ comments }) => {
  const handleSend = () => {
    alert('コメントを送信します');
  };

  return (
    <div>
      <div className="comments">
        {comments.map((c) => (
          <div key={c.id} className="comment">
            <p className="body">{c.body}</p>
          </div>
        ))}
      </div>
      <button className="send-btn" onClick={handleSend}>
        送信
      </button>
    </div>
  );
};

```

## 所感

### ① 移行作業について
旧jQueryコードをChatGPTに投げることで、一定の精度でReactへの変換案が得られました。
ただし、最初から完全に意図通りに動くことは少なく、実際には調整やコード理解の時間が必要でした。

### ② 移行後の開発について
Reactに移行したことで、コードの構造が格段に整理されました。
また、useState や props を使った状態管理により、jQuery時代にありがちだった「フラグ地獄」から解放され、UIロジックも明確に。
その結果、開発効率や保守性も大きく向上しました。

### ③ 今後の開発について
今後は「まずjQueryで作って、あとからReactに移行する」といった流れは極力避けたいと考えています。
現在はAIの支援もあるため、最初から「この機能をReactで作りたい」とAIに指示して開発を進める方が、圧倒的に効率が良いと実感しています。

## 参考
・React公式：既存プロジェクトに React を追加する
https://ja.react.dev/learn/add-react-to-an-existing-project

・White Box技術部：TypeScriptでwindowにプロパティを追加するいくつかの方法
https://seri.hatenablog.com/entry/2020/01/24/152918
※簡略化のために、マウントスクリプトで「(window as any).」を用いる方法について参照しました。