ReactとHTMLの違いとは?JSXの基本を分かりやすく解説

ReactとHTMLの違いからJSXの基本的な書き方まで初心者向けに解説。実際のコード例とともに、HTMLからReactへの移行方法を詳しく説明します。

Learning Next 運営
27 分で読めます

みなさん、Reactを学び始めていますか?

「HTMLは書けるけど、Reactって何が違うの?」「JSXって何?HTMLとどう違うの?」と思ったことはありませんか?

この記事では、ReactとHTMLの基本的な違いからJSXの書き方まで、初心者向けに分かりやすく解説します。 実際のコード例を見ながら、HTMLからReactにスムーズに移行するコツを一緒に学んでいきましょう。

ReactとHTMLって何が違うの?

まずは、ReactとHTMLの根本的な違いを理解しましょう。

HTMLの特徴

HTMLは静的なマークアップ言語です。 一度書いたら、内容は変わりません。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>マイページ</title>
</head>
<body>
    <h1>こんにちは、田中さん</h1>
    <p>今日の天気は晴れです</p>
    <button onclick="showMessage()">クリック</button>
    
    <script>
        function showMessage() {
            alert('ボタンがクリックされました!');
        }
    </script>
</body>
</html>

このHTMLでは、「田中さん」と「晴れ」は固定されています。 変更したい場合は、HTMLファイル自体を書き換える必要があります。

Reactの特徴

Reactは動的なJavaScriptライブラリです。 データが変わると、自動的に表示も更新されます。

import React, { useState } from 'react';

function MyPage() {
  const [userName] = useState('田中さん');
  const [weather] = useState('晴れ');
  const [message, setMessage] = useState('');

  const showMessage = () => {
    setMessage('ボタンがクリックされました!');
  };

  return (
    <div>
      <h1>こんにちは、{userName}</h1>
      <p>今日の天気は{weather}です</p>
      <button onClick={showMessage}>クリック</button>
      {message && <p>{message}</p>}
    </div>
  );
}

Reactでは、{userName}{weather}のように変数を使えます。 データが変わると、画面も自動で更新されるんです。

主な違いをまとめると

HTMLとReactの違いはこんな感じです:

HTML

  • 静的な構造定義
  • 固定された内容
  • ファイル単位での管理
  • シンプルな学習コスト

React

  • 動的なコンポーネント
  • 状態に応じた表示変更
  • コンポーネント単位での管理
  • 再利用可能な部品

簡単に言うと、HTMLは「設計図」で、Reactは「動く機械」のようなイメージです。

JSXって何だろう?

JSXは、JavaScriptの中でHTMLのような記法が使える構文拡張です。

JSXの基本的な書き方

// 基本的なJSX
const element = <h1>Hello, World!</h1>;

// JavaScriptの変数を埋め込み
const name = '田中太郎';
const greeting = <h1>こんにちは、{name}さん!</h1>;

// 複数の要素
const content = (
  <div>
    <h1>タイトル</h1>
    <p>これは段落です。</p>
  </div>
);

{}の中にJavaScriptの式を書くことで、動的な値を表示できます。

HTMLからJSXへの変換例

実際にHTMLからJSXに変換してみましょう:

HTML版

<div class="card">
    <img src="profile.jpg" alt="プロフィール画像">
    <h2>田中太郎</h2>
    <p>Webデベロッパー</p>
    <button onclick="showDetails()">詳細を見る</button>
</div>

JSX版

function ProfileCard() {
  const name = '田中太郎';
  const job = 'Webデベロッパー';
  
  const showDetails = () => {
    alert('詳細を表示します');
  };

  return (
    <div className="card">
      <img src="profile.jpg" alt="プロフィール画像" />
      <h2>{name}</h2>
      <p>{job}</p>
      <button onClick={showDetails}>詳細を見る</button>
    </div>
  );
}

JSXでは、変数を{}で囲んで表示できます。 これで、namejobの値を変えれば、自動で表示も変わります。

JSXの便利な機能

1. JavaScript式の埋め込み

function UserInfo({ user }) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>年齢: {user.age}歳</p>
      <p>職業: {user.job || '未設定'}</p>
      <p>今日は{new Date().toLocaleDateString()}です</p>
    </div>
  );
}

計算結果や関数の戻り値も表示できます。

2. 条件分岐

function WelcomeMessage({ user }) {
  return (
    <div>
      {user ? (
        <h1>おかえりなさい、{user.name}さん!</h1>
      ) : (
        <h1>ログインしてください</h1>
      )}
    </div>
  );
}

ユーザーがログインしているかどうかで、表示を切り替えられます。

3. 配列の表示

function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>{todo}</li>
      ))}
    </ul>
  );
}

配列のデータを繰り返し表示するのも簡単です。

HTMLからJSXに変換するルール

HTMLをJSXに変換する時の重要なルールを覚えましょう。

属性名が変わる

HTMLとJSXでは、一部の属性名が異なります:

// HTML → JSX
class    → className
for      → htmlFor
onclick  → onClick
onchange → onChange
tabindex → tabIndex

実際の例を見てみましょう:

// HTML
<div class="container">
  <label for="email">メール</label>
  <input id="email" onchange="handleChange()">
</div>

// JSX
<div className="container">
  <label htmlFor="email">メール</label>
  <input id="email" onChange={handleChange} />
</div>

classclassNameに、forhtmlForに変わります。

自己終了タグが必須

HTMLでは省略できた自己終了タグが、JSXでは必須になります:

// HTML(自己終了タグは任意)
<img src="image.jpg" alt="画像">
<input type="text" name="username">
<br>
<hr>

// JSX(自己終了タグは必須)
<img src="image.jpg" alt="画像" />
<input type="text" name="username" />
<br />
<hr />

/>を忘れずに付けましょう。

単一のルート要素

JSXでは、必ず一つの要素で囲む必要があります:

// ❌ NG: 複数のルート要素
return (
  <h1>タイトル</h1>
  <p>段落</p>
);

// ✅ OK: 単一要素で囲む
return (
  <div>
    <h1>タイトル</h1>
    <p>段落</p>
  </div>
);

// ✅ OK: React Fragment使用
return (
  <>
    <h1>タイトル</h1>
    <p>段落</p>
  </>
);

余計なdivを作りたくない場合は、<></>(Fragment)を使いましょう。

スタイルの書き方

インラインスタイルの書き方も変わります:

// HTML
<div style="background-color: red; font-size: 16px;">
  スタイル付きテキスト
</div>

// JSX
<div style={{
  backgroundColor: 'red',
  fontSize: '16px'
}}>
  スタイル付きテキスト
</div>

JSXでは、スタイルをオブジェクトとして書きます。 ケバブケース(background-color)はキャメルケース(backgroundColor)に変わります。

実践!HTMLをReactに変換してみよう

実際のHTMLページをReactコンポーネントに変換してみましょう。

プロフィールカードを作る

元のHTML

<!DOCTYPE html>
<html>
<head>
    <title>プロフィール</title>
    <style>
        .profile-card {
            border: 1px solid #ddd;
            border-radius: 8px;
            padding: 20px;
            max-width: 300px;
            text-align: center;
        }
        .avatar {
            width: 100px;
            height: 100px;
            border-radius: 50%;
        }
        .follow-btn {
            background-color: #007bff;
            color: white;
            border: none;
            padding: 10px 20px;
            border-radius: 4px;
            cursor: pointer;
        }
    </style>
</head>
<body>
    <div class="profile-card">
        <img class="avatar" src="avatar.jpg" alt="アバター">
        <h2>田中太郎</h2>
        <p>フロントエンドデベロッパー</p>
        <p>フォロワー: <span id="follower-count">1,234</span>人</p>
        <button class="follow-btn" onclick="toggleFollow()">
            フォローする
        </button>
    </div>

    <script>
        let isFollowing = false;
        let followerCount = 1234;

        function toggleFollow() {
            const button = document.querySelector('.follow-btn');
            const countElement = document.getElementById('follower-count');
            
            if (isFollowing) {
                isFollowing = false;
                followerCount--;
                button.textContent = 'フォローする';
                button.style.backgroundColor = '#007bff';
            } else {
                isFollowing = true;
                followerCount++;
                button.textContent = 'フォロー中';
                button.style.backgroundColor = '#28a745';
            }
            
            countElement.textContent = followerCount.toLocaleString();
        }
    </script>
</body>
</html>

Reactに変換

import React, { useState } from 'react';
import './ProfileCard.css';

function ProfileCard({ user }) {
  const [isFollowing, setIsFollowing] = useState(false);
  const [followerCount, setFollowerCount] = useState(user.followers);

  const toggleFollow = () => {
    if (isFollowing) {
      setIsFollowing(false);
      setFollowerCount(prev => prev - 1);
    } else {
      setIsFollowing(true);
      setFollowerCount(prev => prev + 1);
    }
  };

  return (
    <div className="profile-card">
      <img 
        className="avatar" 
        src={user.avatar} 
        alt={`${user.name}のアバター`} 
      />
      <h2>{user.name}</h2>
      <p>{user.job}</p>
      <p>
        フォロワー: <span>{followerCount.toLocaleString()}</span>人
      </p>
      <button 
        className={`follow-btn ${isFollowing ? 'following' : ''}`}
        onClick={toggleFollow}
      >
        {isFollowing ? 'フォロー中' : 'フォローする'}
      </button>
    </div>
  );
}

function App() {
  const userData = {
    name: '田中太郎',
    job: 'フロントエンドデベロッパー',
    avatar: 'avatar.jpg',
    followers: 1234
  };

  return (
    <div>
      <h1>プロフィール</h1>
      <ProfileCard user={userData} />
    </div>
  );
}

export default App;

HTMLでは直接DOMを操作していましたが、ReactではuseStateを使って状態を管理します。 これで、データが変わると自動で画面も更新されます。

フォームの変換例

元のHTML

<form onsubmit="handleSubmit(event)">
    <div>
        <label for="name">名前:</label>
        <input type="text" id="name" name="name" required>
    </div>
    <div>
        <label for="email">メール:</label>
        <input type="email" id="email" name="email" required>
    </div>
    <button type="submit">送信</button>
</form>

<script>
    function handleSubmit(event) {
        event.preventDefault();
        const formData = new FormData(event.target);
        const data = Object.fromEntries(formData);
        console.log('送信データ:', data);
        alert('送信完了!');
    }
</script>

Reactに変換

import React, { useState } from 'react';

function ContactForm() {
  const [formData, setFormData] = useState({
    name: '',
    email: ''
  });

  const [errors, setErrors] = useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setFormData(prev => ({
      ...prev,
      [name]: value
    }));
    
    if (errors[name]) {
      setErrors(prev => ({
        ...prev,
        [name]: ''
      }));
    }
  };

  const validateForm = () => {
    const newErrors = {};
    
    if (!formData.name.trim()) {
      newErrors.name = '名前は必須です';
    }
    
    if (!formData.email.trim()) {
      newErrors.email = 'メールアドレスは必須です';
    } else if (!/\S+@\S+\.\S+/.test(formData.email)) {
      newErrors.email = 'メールアドレスの形式が正しくありません';
    }
    
    return newErrors;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    
    const newErrors = validateForm();
    if (Object.keys(newErrors).length > 0) {
      setErrors(newErrors);
      return;
    }

    console.log('送信データ:', formData);
    alert('送信完了!');
    
    setFormData({ name: '', email: '' });
  };

  return (
    <form onSubmit={handleSubmit}>
      <div>
        <label htmlFor="name">名前:</label>
        <input
          type="text"
          id="name"
          name="name"
          value={formData.name}
          onChange={handleChange}
          required
        />
        {errors.name && <span className="error">{errors.name}</span>}
      </div>
      
      <div>
        <label htmlFor="email">メール:</label>
        <input
          type="email"
          id="email"
          name="email"
          value={formData.email}
          onChange={handleChange}
          required
        />
        {errors.email && <span className="error">{errors.email}</span>}
      </div>
      
      <button type="submit">送信</button>
    </form>
  );
}

export default ContactForm;

Reactでは、フォームの値をstateで管理します。 バリデーション結果もリアルタイムで表示できるので、ユーザーにとって使いやすいフォームになります。

コンポーネント化のメリット

HTMLをReactに変換する時の最大のメリットはコンポーネント化です。

繰り返し部分をコンポーネント化

HTML(繰り返しコード)

<div class="product-list">
    <div class="product-item">
        <img src="product1.jpg" alt="商品1">
        <h3>商品1</h3>
        <p>¥1,000</p>
        <button onclick="addToCart(1)">カートに追加</button>
    </div>
    
    <div class="product-item">
        <img src="product2.jpg" alt="商品2">
        <h3>商品2</h3>
        <p>¥2,000</p>
        <button onclick="addToCart(2)">カートに追加</button>
    </div>
    
    <div class="product-item">
        <img src="product3.jpg" alt="商品3">
        <h3>商品3</h3>
        <p>¥3,000</p>
        <button onclick="addToCart(3)">カートに追加</button>
    </div>
</div>

React(コンポーネント化)

function ProductItem({ product, onAddToCart }) {
  return (
    <div className="product-item">
      <img src={product.image} alt={product.name} />
      <h3>{product.name}</h3>
      <p>¥{product.price.toLocaleString()}</p>
      <button onClick={() => onAddToCart(product.id)}>
        カートに追加
      </button>
    </div>
  );
}

function ProductList({ products, onAddToCart }) {
  return (
    <div className="product-list">
      {products.map(product => (
        <ProductItem
          key={product.id}
          product={product}
          onAddToCart={onAddToCart}
        />
      ))}
    </div>
  );
}

function App() {
  const products = [
    { id: 1, name: '商品1', price: 1000, image: 'product1.jpg' },
    { id: 2, name: '商品2', price: 2000, image: 'product2.jpg' },
    { id: 3, name: '商品3', price: 3000, image: 'product3.jpg' }
  ];

  const handleAddToCart = (productId) => {
    console.log(`商品ID: ${productId} をカートに追加しました`);
  };

  return (
    <div>
      <h1>商品一覧</h1>
      <ProductList products={products} onAddToCart={handleAddToCart} />
    </div>
  );
}

コンポーネント化することで、同じコードを何度も書く必要がなくなります。 商品が100個あっても、配列にデータを追加するだけで済みます。

よくある間違いと対処法

HTMLからReactに移行する時によくある間違いを見てみましょう。

1. クラス名の間違い

// ❌ NG: HTMLの書き方
<div class="container">
  <p>テキスト</p>
</div>

// ✅ OK: JSXの書き方
<div className="container">
  <p>テキスト</p>
</div>

classclassNameに変更しましょう。

2. イベントハンドラーの間違い

// ❌ NG: 文字列での指定
<button onclick="handleClick()">クリック</button>

// ✅ OK: 関数での指定
<button onClick={handleClick}>クリック</button>

// ✅ OK: 無名関数での指定
<button onClick={() => handleClick('引数')}>クリック</button>

イベントハンドラーは関数として渡しましょう。

3. 自己終了タグの忘れ

// ❌ NG: 終了タグなし
<img src="image.jpg" alt="画像">
<input type="text">

// ✅ OK: 自己終了タグ
<img src="image.jpg" alt="画像" />
<input type="text" />

JSXでは自己終了タグが必須です。

4. 状態管理の間違い

// ❌ NG: 直接的な値の変更
function Counter() {
  let count = 0;
  
  const increment = () => {
    count = count + 1; // 画面が更新されない
  };
  
  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={increment}>増加</button>
    </div>
  );
}

// ✅ OK: useStateを使用
function Counter() {
  const [count, setCount] = useState(0);
  
  const increment = () => {
    setCount(count + 1); // 正しく画面が更新される
  };
  
  return (
    <div>
      <p>カウント: {count}</p>
      <button onClick={increment}>増加</button>
    </div>
  );
}

値を変更する時は、必ずuseStateを使いましょう。

まとめ

HTMLとReactの違いから、JSXの基本的な書き方まで詳しく学習しました。

覚えておきたいポイント

  • HTMLは静的、Reactは動的
  • JSXでは{}内にJavaScript式を書ける
  • 属性名が一部変わる(classclassNameなど)
  • 自己終了タグが必須
  • 状態管理はuseStateを使う

HTMLからReactへの移行手順

  1. 属性名を変更する
  2. 自己終了タグを追加する
  3. 単一要素で囲む
  4. 動的な部分を変数にする
  5. 状態管理をuseStateで行う

HTMLの知識があれば、Reactへの移行はそれほど難しくありません。 ぜひ実際にコードを書いて、JSXの書き方に慣れていってくださいね!

関連記事