VanillaなJavaScriptからReactを使い出した時の前後のコードを見つめてみる

# 初めに

ただの記録です。
Reactに移る前のコードと移った後のコードを貼っているだけ。

# VanilaなJavaScriptでの記載

## 移行前の前(createElementを多用)

`document.createElement`をひたすら使ってるパターンです
bodyの中身が全部tdでいいならさらにmapでいいんですが、
各カラムで別々の何かを適用させたい場合はひたすら変数が増えていくパターン...(嫌すぎる)

<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="JjvjVxv" data-user="sky-sora" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sky-sora/pen/JjvjVxv">
  Untitled</a> by sinsky (<a href="https://codepen.io/sky-sora">@sky-sora</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

<details>
  <summary>コード(codepenの内容と一緒です)</summary>
  <div>

```html
<div id="root"></div>
```
```javascript
window.addEventListener("DOMContentLoaded", () => {
  // 使用するデータ
  // https://www.tiobe.com/tiobe-index/
  const tableData = {
    head: ["Aug 2022", "Aug 2021", "Change", "Programming Language", "Ratings", "Change"],
    body: [
      [1, 2, "up", "Python", 15.42, "+3.56"],
      [2, 1, "down", "C", 14.59, "+2.03"],
      [3, 3, "", "Java", 12.40, "+1.96"],
      [4, 4, "", "C++", 10.17, "+2.81"],
      [5, 5, "", "C#", 5.59, "+0.45"],
      [6, 6, "", "Visual Basic", 4.99, "0.33"],
      [7, 7, "", "JavaSCript", 2.33, "-0.61"],
      [8, 9, "up", "Assembly language", 2.17, "+0.14"],
      [9, 10, "up", "SQL", 1.70, "+0.23"],
      [10, 8, "down", "PHP", 1.39, "-0.80"]
    ],
  };
  const root = document.querySelector("#root");
  const table = document.createElement("table");

  const thead = document.createElement("thead");
  const thead_tr = document.createElement("tr");
  tableData.head.map(title => {
    const th = document.createElement("th");
    th.innerHTML = title;
    thead_tr.append(th);
  });
  thead.append(thead_tr);
  table.append(thead);

  const tbody = document.createElement("tbody");
  tableData.body.map(row => {
    const tr = document.createElement("tr");

    const [year, lastYear, change, lang, rate, diff] = row;
    const year_td = document.createElement("td");
    year_td.innerHTML = year;
    tr.append(year_td);
    const lastYear_td = document.createElement("td");
    lastYear_td.innerHTML = lastYear;
    tr.append(lastYear_td);
    const change_td = document.createElement("td");
    change_td.innerHTML = change;
    tr.append(change_td);
    const lang_td = document.createElement("td");
    lang_td.innerHTML = lang;
    tr.append(lang_td);
    const rate_td = document.createElement("td");
    rate_td.innerHTML = rate;
    tr.append(rate_td);
    const diff_td = document.createElement("td");
    diff_td.innerHTML = diff;
    tr.append(diff_td);
    tbody.append(tr);
  });
  table.append(tbody);
  root.append(table);
})
```

  </div>
</details>


## 移行前(templateを利用する)

`document.importNode`を利用して、HTMLに先に記載しているTemplateを使うやり方です
一つ前のやり方よりも、こっちの方がclassの定義とかを前もってできるので、
わざわざJavaScriptで書き換える必要がないというメリットがあります。
これを知ってからは`createElement`を使うことは無くなりました(HTMLでclassとかdata属性を管理したかったので)

<p class="codepen" data-height="300" data-default-tab="js,result" data-slug-hash="BaxaeQK" data-user="sky-sora" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/sky-sora/pen/BaxaeQK">
  use template</a> by sinsky (<a href="https://codepen.io/sky-sora">@sky-sora</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

<details>
  <summary>コード(codepenの内容と一緒です)</summary>
  <div>

```html
<div id="root">
  <table id="table">
    <thead></thear>
    <tbody></tbody>
  </table>
</div>
<template id="table-head">
  <tr>
    <th data-id="year"></th>
    <th data-id="lastYear"></th>
    <th data-id="change"></th>
    <th data-id="lang"></th>
    <th data-id="rate"></th>
    <th data-id="diff"></th>
  </tr>
</template>
<template id="table-body">
  <tr>
    <td data-id="year"></td>
    <td data-id="lastYear"></td>
    <td data-id="change"></td>
    <td data-id="lang"></td>
    <td data-id="rate"></td>
    <td data-id="diff"></td>
  </tr>
</template>
```
```javascript
window.addEventListener("DOMContentLoaded", () => {
  // 使用するデータ
  // https://www.tiobe.com/tiobe-index/
  const tableData = {
    head: ["Aug 2022", "Aug 2021", "Change", "Programming Language", "Ratings", "Change"],
    body: [
      [1, 2, "up", "Python", 15.42, "+3.56"],
      [2, 1, "down", "C", 14.59, "+2.03"],
      [3, 3, "", "Java", 12.40, "+1.96"],
      [4, 4, "", "C++", 10.17, "+2.81"],
      [5, 5, "", "C#", 5.59, "+0.45"],
      [6, 6, "", "Visual Basic", 4.99, "0.33"],
      [7, 7, "", "JavaSCript", 2.33, "-0.61"],
      [8, 9, "up", "Assembly language", 2.17, "+0.14"],
      [9, 10, "up", "SQL", 1.70, "+0.23"],
      [10, 8, "down", "PHP", 1.39, "-0.80"]
    ],
  };

  const table_head_content = document.querySelector("#table-head").content;
  const head_fragment = document.createDocumentFragment();

  const head_clone = document.importNode(table_head_content, true);
  const [year, lastYear, change, lang, rate, diff] = tableData.head;
  head_clone.querySelector("[data-id='year']").innerHTML = year;
  head_clone.querySelector("[data-id='lastYear']").innerHTML = lastYear;
  head_clone.querySelector("[data-id='change']").innerHTML = change;
  head_clone.querySelector("[data-id='lang']").innerHTML = lang;
  head_clone.querySelector("[data-id='rate']").innerHTML = rate;
  head_clone.querySelector("[data-id='diff']").innerHTML = diff;
  head_fragment.append(head_clone);
  document.querySelector('#table > thead').appendChild(head_fragment);

  // 長いから関数作ってみる
  const getId = (clone, id) => clone.querySelector(`[data-id='${id}']`);

  const table_body_content = document.querySelector("#table-body").content;
  const body_fragment = document.createDocumentFragment();
  tableData.body.map(row => {
    const body_clone = document.importNode(table_body_content, true);
    const [year, lastYear, change, lang, rate, diff] = row;
    getId(body_clone, "year").innerHTML = year;
    getId(body_clone, "lastYear").innerHTML = lastYear;
    getId(body_clone, "change").innerHTML = change;
    getId(body_clone, "lang").innerHTML = lang;
    getId(body_clone, "rate").innerHTML = rate;
    getId(body_clone, "diff").innerHTML = diff;
    body_fragment.append(body_clone);
  });
  document.querySelector('#table > tbody').appendChild(body_fragment);
});

```

  </div>
</details>

# Reactで書く

面倒になったのでコードだけな上に一つのコードにまとめています(しかもexportして終わり)

```jsx
const Thead = ({ data }) => {
  const [year, lastYear, change, lang, rate, diff] = data;
  return (
    <thead>
      <tr>
        <th>{year}</th>
        <th>{lastYear}</th>
        <th>{change}</th>
        <th>{lang}</th>
        <th>{rate}</th>
        <th>{diff}</th>
      </tr>
    </thead>
  );
};
const Tbody = ({ data }) => {
  return (
    <tbody>
      {data.map((row) => {
        const [year, lastYear, change, lang, rate, diff] = row;
        return (
          <tr>
            <td>{year}</td>
            <td>{lastYear}</td>
            <td>{change}</td>
            <td>{lang}</td>
            <td>{rate}</td>
            <td>{diff}</td>
          </tr>
        );
      })}
    </tbody>
  );
};
export const App = () => {
  const tableData = {
    head: ["Aug 2022", "Aug 2021", "Change", "Programming Language", "Ratings", "Change"],
    body: [
      [1, 2, "up", "Python", 15.42, "+3.56"],
      [2, 1, "down", "C", 14.59, "+2.03"],
      [3, 3, "", "Java", 12.4, "+1.96"],
      [4, 4, "", "C++", 10.17, "+2.81"],
      [5, 5, "", "C#", 5.59, "+0.45"],
      [6, 6, "", "Visual Basic", 4.99, "0.33"],
      [7, 7, "", "JavaSCript", 2.33, "-0.61"],
      [8, 9, "up", "Assembly language", 2.17, "+0.14"],
      [9, 10, "up", "SQL", 1.7, "+0.23"],
      [10, 8, "down", "PHP", 1.39, "-0.80"],
    ],
  };
  return (
    <table>
      <Thead data={tableData.head} />
      <Tbody data={tableData.body} />
    </table>
  );
};

```


# 終わりに

こんな感じで移行したんだよってお話でした。
`document.importNode`に慣れていたからか、
React(JSX)の方にも違和感なく書き換えることができました。
`document.createElement`の多用は何も考えずにJavaScriptだけ弄っていればいいので楽なんですが、
最終的なHTMLが想像しづらい上に、classなどの管理も非常に面倒だったなぁと書きながら思い出していました。

React言わずもがなですね。。。

Vanillaは気軽なモックを作るときにとてもお手軽なので、
Node.jsが使えない環境の時はとてもありがたいんですよね。
(CDNはBabelも導入しないとJSXが書けなかったり、CDNが業務上ネットワークでブロックされていたり(思い出したくない記憶))...
忘れないための備忘録的な意味も含めての投稿でした。