はじめに
HTML ではクラス名やインラインスタイルを指定する場合、次のように書くことができます。
<div class="your-name" style="color: #FF0000;">
Hello World!
</div>
一方 React (JSX) では次のようになります。
<div className="your-name" style={{ 'color': '#FF0000' }}>
Hello World!
</div>
公式のドキュメントにも、React と HTML で挙動が異なる属性がいくつか存在すると書かれています。
以上から、React の className 属性や style 属性は HTML とは異なるものであり、そういう意味では、これらは React の拡張であると言えそうです。
他にも onChange 属性、select/textarea タグの value/defaultValue 属性など、HTML と挙動が異なる属性が多数存在しています。
(JSX の機能なので React ではなく JSX 拡張と呼ぶべきではないか? というのはその通りかもしれません。)
React ではなく DOM の拡張?
しかし、className という名前や、インラインスタイルを文字列以外で指定するというのは、React ではなくてももともと 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 は使用されていません 。環境によってはそのような変換結果になることもあるでしょう。)
l.createElement(
"div",
{
className: "your-class-name",
style: {color:"#FF0000"}
},
"Hello World!"
),
l.createElement
は恐らく React.createElement
のことでしょう。しかしこの createElement メソッドは JavaScript の DOM にもともと存在している同名のメソッドとは異なるため、その意味ではこれは「Reactの」機能(またはJSXの機能)であると言えるのではないでしょうか。
まとめ
className 属性 や style 属性など、React では HTML とは挙動が異なるものが存在しています。また、JavaScript の createElement にもこれらの属性を指定する機能はありません。従って、これらの属性は React (JSX) の拡張であると言えそうです。(個人の感想です)
おまけ
最後に HTML と各フレームワークの比較を載せておきます。こうして見ると DOM と React が近い設計になっていることがわかります。
クラス名とインラインスタイルの指定
HTML
<div class="your-name" style="color: #FF0000;">
Hello World!
</div>
DOM
element.className
は文字列なので React と同じ形式です。
一方で、element.style
そのものにオブジェクトを代入してスタイルを適用することはできませんでした。
const element = document.querySelectorAll('div')[0];
element.style.color = '#00FF00';
element.className = 'another-class-name';
React / JSX
Vue + JSX でもほぼ同じ書き方ができるようです。
<div className="your-class-name" style={{ 'color': '#FF0000' }}>
Hello World!
</div>
Vue
Vue では v-bind:
を付けて JavaScript のコードを書くこともできますし、通常の HTML と同様の書き方をすることもできます。
<div class="your-name" style="color: #FF0000">
Hello World!
</div>
または
<div v-bind:class="dynamicClass"
v-bind:style="{ color: dynamicColor }">
Dynamic Attributes!
</div>
フォーム要素
HTML
<select>
<option value="apple">りんご</option>
<option value="orange" selected>みかん</option>
</select>
<textarea>Hello World</textarea>
<input type="checkbox" checked />チェックボックス
DOM
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
<select value={myFruit}>
<option value="apple">りんご</option>
<option value="orange">みかん</option>
</select>
<textarea value={myText} />
<input type="checkbox" checked={isChecked} />チェックボックス
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
<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
で処理をキャンセルすることはできません。
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 とは異なり、合成イベント と呼ばれるものだそうです。それでも依然として e.preventDefault()
や e.stopPropagation()
などを使用することができます。
また、テキストボックスでの onChange の挙動は JavaScript の onInput に近いものになっており、一文字タイプするごとにイベントハンドラが呼ばれます。
<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()
相当になります。
<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>