React の className 属性 や style 属性は React 拡張なのか？

# はじめに

HTML ではクラス名やインラインスタイルを指定する場合、次のように書くことができます。

```html
<div class="your-name" style="color: #FF0000;">
  Hello World!
</div>
```

一方 React (JSX) では次のようになります。

```jsx
<div className="your-name" style={{ 'color': '#FF0000' }}>
  Hello World!
</div>
```

公式のドキュメントにも、***React と HTML で挙動が異なる属性がいくつか存在***すると書かれています。

https://ja.legacy.reactjs.org/docs/dom-elements.html#differences-in-attributes

以上から、React の className 属性や style 属性は HTML とは異なるものであり、そういう意味では、これらは React の拡張であると言えそうです。

他にも onChange 属性、select/textarea タグの value/defaultValue 属性など、HTML と挙動が異なる属性が多数存在しています。

（JSX の機能なので React ではなく JSX 拡張と呼ぶべきではないか？ というのはその通りかもしれません。）

# React ではなく DOM の拡張？

しかし、className という名前や、インラインスタイルを文字列以外で指定するというのは、React ではなくてももともと JavaScript に存在していたものだそうです。

```javascript
document.querySelectorAll('div')[0].style.color = '#00FF00';
document.querySelectorAll('div')[0].className = 'another-class-name';
```

ということは、これらは React の拡張ではないのでしょうか？ これを調べるために、JSX のトランスパイル結果を見てみましょう。（なお、この例では React 17.0.2 を使用していますが、[React 17 の新機能の _jsx や _jsxs は使用されていません](https://zenn.dev/uhyo/articles/react17-new-jsx-transform "React17におけるJSXの新しい変換を理解する") 。環境によってはそのような変換結果になることもあるでしょう。）

```javascript
l.createElement(
  "div",
  {
    className: "your-class-name",
    style: {color:"#FF0000"}
  },
  "Hello World!"
),
```

`l.createElement` は恐らく `React.createElement` のことでしょう。しかしこの createElement メソッドは [JavaScript の DOM にもともと存在している同名のメソッド](https://developer.mozilla.org/ja/docs/Web/API/Document/createElement "Document.createElement() - Web API | MDN")とは異なるため、その意味ではこれは「Reactの」機能（またはJSXの機能）であると言えるのではないでしょうか。

# まとめ

className 属性 や style 属性など、React では HTML とは挙動が異なるものが存在しています。また、JavaScript の createElement にもこれらの属性を指定する機能はありません。従って、これらの属性は React (JSX) の拡張であると言えそうです。（個人の感想です）

# おまけ

最後に HTML と各フレームワークの比較を載せておきます。こうして見ると DOM と React が近い設計になっていることがわかります。

## クラス名とインラインスタイルの指定

### HTML

```html
<div class="your-name" style="color: #FF0000;">
  Hello World!
</div>
```

## DOM

`element.className` は文字列なので React と同じ形式です。
一方で、`element.style` そのものにオブジェクトを代入してスタイルを適用することはできませんでした。

```javascript
const element = document.querySelectorAll('div')[0];

element.style.color = '#00FF00';
element.className = 'another-class-name';
```

### React / JSX

Vue + JSX でもほぼ同じ書き方ができるようです。

```jsx
<div className="your-class-name" style={{ 'color': '#FF0000' }}>
  Hello World!
</div>
```

## Vue

Vue では `v-bind:` を付けて JavaScript のコードを書くこともできますし、通常の HTML と同様の書き方をすることもできます。

```vue
<div class="your-name" style="color: #FF0000">
  Hello World!
</div>
または
<div v-bind:class="dynamicClass"
     v-bind:style="{ color: dynamicColor }">
  Dynamic Attributes!
</div>
```

## フォーム要素

### HTML

```html
<select>
  <option value="apple">りんご</option>
  <option value="orange" selected>みかん</option>
</select>
<textarea>Hello World</textarea>
<input type="checkbox" checked />チェックボックス
```

## DOM

```javascript
const selectElement = document.querySelectorAll('select')[0];
const textareaElement = document.querySelectorAll('textarea')[0];
const checkboxElement = document.querySelectorAll('input[type="checkbox"]')[0];

selectElement.value = "apple";
textareaElement.value = "Enjoy JavaScript!";
checkboxElement.checked = false;
```

### React / JSX

```jsx
<select value={myFruit}>
  <option value="apple">りんご</option>
  <option value="orange">みかん</option>
</select>
<textarea value={myText} />
<input type="checkbox" checked={isChecked} />チェックボックス
```

### Vue

```vue
<select v-bind:value="myFruit">
  <option value="apple">りんご</option>
  <option value="orange">みかん</option>
</select>
<textarea v-bind:value="myText" />
<input type="checkbox" v-bind:checked="isChecked" />チェックボックス
```

または、`v-bind:` の代わりに `v-model` で双方向データバインディングすることもできます。

## イベントハンドラ

### HTML

```html
<form action="./submit"
      onSubmit="console.log('submit'); return false;">
  <input type="text"
         onChange="console.log('change')"
         onInput="console.log('input')" />
  <button type="submit">送信</button>
</form>
```

### DOM

`addEventListener` を使用する場合は、`return false` で処理をキャンセルすることはできません。

```javascript
const formElement = document.querySelectorAll('form')[0];
const inputElement = document.querySelectorAll('input[type="text"]')[0];

inputElement.addEventListener("change", () => { console.log('change'); }); 
inputElement.addEventListener("input", () => { console.log('input'); }); 
formElement.addEventListener("submit", (e) => {
  console.log('submit');
  e.preventDefault();
}); 
```

### React / JSX

`return false;` でフォームの送信を中止できない点が DOM の仕様と似ていますね。ですが、引数として渡されるのは [DOM で実装されている Event](https://developer.mozilla.org/ja/docs/Web/API/Event "Event - Web API | MDN") とは異なり、[合成イベント](https://ja.legacy.reactjs.org/docs/events.html "合成イベント (SyntheticEvent) – React") と呼ばれるものだそうです。それでも依然として `e.preventDefault()` や `e.stopPropagation()` などを使用することができます。

また、テキストボックスでの onChange の挙動は JavaScript の onInput に近いものになっており、一文字タイプするごとにイベントハンドラが呼ばれます。

```jsx
<form action="./submit"
      onSubmit={(e) => { console.log('submit'); e.preventDefault(); }}>
  <input type="text"
         onChange={() => { console.log('change'); }} />
  <button type="submit">送信</button>
</form>
```

### Vue

`.prevent` で `e.preventDefault()` 相当になります。

```vue
<form action="./submit"
      v-on:submit.prevent="log += 'submit!'">
  <input type="text"
         v-on:change="log += 'change!'"
         v-on:input="log += 'input!'" />
  <button type="submit">送信</button>
  <p>{{ log }}</p>
</form>
```