React Vite でチュートリアル
#プログラミング

created: 2026/01/04, modified: 2026/01/04

標準のサムネイル 2024-06

React Vite でチュートリアル

React プロジェクトを作る(Vite 版:軽くておすすめ)

cd /var/www/django-5

# Vite を使って React プロジェクト作成
# プロンプトが出たら:
# - Framework: React
# - Variant: JavaScript (でOK)
npm create vite@latest frontend

cd frontend
npm install

`npm install`を実行すると、package.jsonファイルに記載されているすべての依存パッケージがインストールされます。

開発サーバーで React を動かす

cd /var/www/django-5/frontend
npm run dev -- --host 0.0.0.0 --port 3000
  VITE v7.3.0  ready in 353 ms

  ➜  Local:   http://localhost:3000/
  ➜  Network: http://172.31.35.28:3000/
  ➜  press h + enter to show help

開発サーバーの稼働確認方法

# lsof コマンドで確認
# ポート3000を使用しているプロセスを確認
lsof -i :3000

# netstat コマンドで確認
netstat -tuln | grep 3000

出力例:

$ lsof -i :3000
COMMAND      PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
MainThrea 125976 ec2-user   31u  IPv4 364804      0t0  TCP *:hbci (LISTEN)

$ netstat -tuln | grep 3000
tcp        0      0 0.0.0.0:3000            0.0.0.0:*               LISTEN

このような出力が表示されれば、ポート3000でサーバーが正常に稼働しています。

  • `LISTEN` という状態は、サーバーが接続を待ち受けている(稼働中)ことを示します
  • `0.0.0.0:3000` は、すべてのネットワークインターフェースでポート3000をリッスンしていることを意味します

稼働中のターミナルを閉じてしまった場合の対処法

プロセスが残っている場合

# プロセスIDを確認(lsof の出力の PID 列)
lsof -i :3000
COMMAND      PID     USER   FD   TYPE DEVICE SIZE/OFF NODE NAME
MainThrea 125976 ec2-user   31u  IPv4 364804      0t0  TCP *:hbci (LISTEN)

# プロセスを停止(PIDの部分は上記で確認した番号に置き換える)
kill -9 <PID>

# 例: kill -9 125976

セキュリティグループ

IP指定方法、AWSのセキュリティグループ

特定のIPアドレスのみ許可する方法

自分のPCのIPアドレス(`20.234.123.56`)のみを許可する場合:

20.234.123.56/32

重要: 末尾に`/32`を付けることで、そのIPアドレス1つだけを指定できます。

複数のIPアドレスを許可する方法

複数のIPアドレスを許可したい場合は、ルールを複数追加します。

方法1: 同じポートに対して複数のルールを追加

ルール1: タイプ=カスタムTCP, ポート=3000, ソース=20.234.123.56/32
ルール2: タイプ=カスタムTCP, ポート=3000, ソース=203.0.113.25/32
ルール3: タイプ=カスタムTCP, ポート=3000, ソース=198.51.100.42/32

IPは通ったがエラー

下記でアクセスするとエラーがでました。
http://django-5.usual.tools:3000/

Blocked request. This host ("django-5.usual.tools") is not allowed.
To allow this host, add "django-5.usual.tools" to `server.allowedHosts` in vite.config.js.

解決方法: vite.config.js に allowedHosts を追加

Viteの設定ファイルを編集して、外部ドメインからのアクセスを許可する必要があります。

  1. vite.config.js を編集
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

// https://vite.dev/config/
export default defineConfig({
  plugins: [react()],
  server: {
    host: '0.0.0.0',
    port: 3000,
    allowedHosts: [
      'django-5.usual.tools', // 追加
      'localhost'
    ]
  }
})

開発サーバーを再起動しなくてもいけました

基礎的なことReact

JSX とは

src/App.jsx の拡張子 jsx は何者?react 専用?

JSX とは?

JSX(JavaScript XML)は、JavaScriptの構文拡張です。HTMLのような構文でUIを記述できるようにするものです。

React 専用?

JSXは主にReactで使用されますが、React専用ではありません。他のライブラリやフレームワーク(例:Preact、Solid.js)でも使用できます。

JSX の特徴

  • HTMLライクな構文: JavaScriptの中にHTMLのようなタグを書けます
  • JavaScriptの式を埋め込める: `{'{}'}` 内にJavaScriptの式を書けます
  • コンパイルが必要: ブラウザは直接JSXを理解できないため、Babelなどでコンパイルします

.jsx と .js の違い

技術的には`.js`ファイルでもJSXを書けますが、`.jsx`拡張子を使うことで:

  • ファイルにJSXが含まれていることが明確になる
  • エディタがJSXのシンタックスハイライトを適切に表示できる
  • 開発ツールが適切に処理できる

JSX の例

// JSXを使った例
function Welcome() {
  const name = "Kenji";
  return (
    <div>
      <h1>こんにちは、{name}さん!</h1>
      <p>Reactへようこそ</p>
    </div>
  );
}

// これは以下のようにコンパイルされます
function Welcome() {
  const name = "Kenji";
  return React.createElement(
    'div',
    null,
    React.createElement('h1', null, 'こんにちは、', name, 'さん!'),
    React.createElement('p', null, 'Reactへようこそ')
  );
}

JSX / React でのコメントの書き方

{/* カウンター */}
はどういうこと?reactのコメントは{}がひつようってこと?

Reactの`return`文内(JSX内)では、通常のJavaScriptコメント`// コメント`や`/* コメント */`は使えません。JSX内でコメントを書く場合は、JavaScriptの式として評価される必要があるため、`{'{}'}`で囲む必要があります。

JSX内のコメント構文

function App() {
  return (
    <div>
      {/* これがJSX内のコメントです */}
      <h1>こんにちは</h1>
      
      {/* 
        複数行の
        コメントも
        このように書けます
      */}
      <p>テキスト</p>
    </div>
  );
}

なぜ {} が必要なのか?

JSXはHTMLのように見えますが、実際にはJavaScriptです。JSX内で`{'{}'}`を使うと、その中身は「JavaScriptの式」として評価されます。コメント`/* */`もJavaScriptの構文なので、`{'{}'}`で囲む必要があります。

JSX外(return文の外)のコメント

JSXの外、つまり通常のJavaScript部分では、普通のコメント構文が使えます:

function App() {
  // これは通常のJavaScriptコメント(JSXの外)
  const message = "こんにちは";
  
  /* これも普通のコメント */
  
  return (
    <div>
      {/* JSX内ではこの書き方 */}
      <h1>{message}</h1>
    </div>
  );
}

まとめ

  • JSX内(return文のHTMLっぽい部分): `{'{/* コメント */'}`
  • JSX外(通常のJavaScript部分): `// コメント` または `/* コメント */`

useStateとuseEffect

`useState`と`useEffect`はReactのフック (Hooks)で、`useState`はコンポーネントの状態(データ)を管理し、`useEffect`は副作用(データ取得、DOM操作、タイマー設定など)を実行します。`useState`で状態を更新すると、その変更をトリガーに`useEffect`が実行され、UIの更新と同期的な処理を連携させることがReactの基本的な動作パターンです。

React の基本とuseState / useEffect

1. Vite + React プロジェクトのざっくり構成

`/var/www/django-5/frontend` の中身はだいたいこんな感じです:

frontend/
├── index.html
├── vite.config.js
├── package.json
└── src/
    ├── main.jsx
    ├── App.jsx
    ├── assets/
    └── ...

よく見るのはこの4つ:

  1. `index.html`
  2. - ブラウザに最初に返される「土台のHTML」
  3. - `<div id="root"></div>` が置いてあって、Reactはここに「入り込む」
  4. `src/main.jsx`
  5. - Reactアプリのエントリーポイント
  6. - `index.html` の `#root` に対して `<App />` を描画している
  7. `src/App.jsx`
  8. - いま触る「メインのReactコンポーネント」
  9. - ここを書き換えると画面が変わる
  10. `package.json`
  11. - 使っているライブラリ一覧と、`npm run dev` などのスクリプト定義

`index.html` のイメージ

最低限ここだけ押さえておけばOKです:

<body>
  <div id="root"></div>
  <script type="module" src="/src/main.jsx"></script>
</body>
  • Reactは `#root` に対して描画します
  • 実際の「中身」は JS 側(`main.jsx` / `App.jsx`)で作る

---

2. `main.jsx` と `App.jsx` の関係

`src/main.jsx`

よくある形はこんな感じです(Viteのデフォルトから簡略):

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App.jsx'

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>,
)

やっていること:

  • `document.getElementById('root')`
  • → `index.html` の `<div id="root"></div>` を取得
  • そこに `<App />` コンポーネントを描画

`src/App.jsx`

最初はこんな感じのコードが入っているはずです(多少違ってもOK):

import { useState } from 'react'

function App() {
  const [count, setCount] = useState(0)

  return (
    <div>
      <h1>Vite + React</h1>
      <button onClick={() => setCount(count + 1)}>
        count is {count}
      </button>
    </div>
  )
}

export default App

ここで初登場が `useState` です。

次でここを分解します。

3. useState

3-1. まずは構文

const [count, setCount] = useState(0)

「`count` という状態と、それを書き換える関数 `setCount` を用意して、最初の値は 0 にしておいて」

  • `count`
  • → 画面に表示したい「状態」
  • → 例: カウンターの現在値
  • `setCount`
  • → `count` を更新するための専用関数
  • → `setCount(1)` と呼ぶと「新しい値 1 で再描画して」とReactに頼むイメージ
  • `useState(0)` の `(0)`
  • → 「初期値 0」という意味

3-2. なぜ普通の変数ではダメなのか

let count = 0

function App() {
  count = count + 1   // ← これをやっても画面は勝手に更新されない
}
  • 普通の変数を書き換えても「Reactは気づかない」ので画面が変わらない

`useState` を使うと

  • 「状態が変わったよ」とReactが把握
  • 必要な部分だけ描画し直してくれる

ポイント:

  • 「画面に関係する値」は `useState` で持つ
  • `setXXX` したら、その変更に合わせて画面を再描画してくれる

4. useEffect

まず、よく見るテンプレート:

import { useState, useEffect } from 'react'

function App() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    console.log('count が変わったタイミング or 初回レンダーで実行')
  }, [count])

  return (
    <div>...</div>
  )
}

4-1. useEffect とは何か

「レンダリングのあとに『副作用(サイドエフェクト)』を実行するためのフック」

副作用の例:

  • コンソールログ
  • APIリクエスト(fetch)
  • `setInterval` / `setTimeout`
  • ブラウザのタイトルを書き換え(`document.title = ...`)

4-2. 3パターンだけ覚えればOK

パターン1:依存配列なし

useEffect(() => {
  console.log('毎回レンダー後に実行される')
})
  • 依存配列 `[]` を書かない
  • → コンポーネントが再描画されるたびに毎回実行

パターン2:`[]`(空配列)

useEffect(() => {
  console.log('最初の1回だけ実行')
}, [])
  • 「初回マウント時だけ1回実行」
  • 初期読み込み時にだけ何かしたいときに使う
  • → 例: 初回だけAPIからデータを取ってくる

パターン3:`[count]` のように値を指定

useEffect(() => {
  console.log(`count が ${count} に変わったときに実行`)
}, [count])
  • `count` が変わるたびに実行
  • 「特定の状態が変わったときだけ何かしたい」ケースに使う

5. 実際に書いてみるミニサンプル

`src/App.jsx` を、学習用に一旦こう書き換えてみると分かりやすいです。

import React, { useState, useEffect } from 'react'

function App() {
  // ① カウンター状態
  const [count, setCount] = useState(0)

  // ② テキストの状態
  const [text, setText] = useState('')

  // ③ 初回だけ実行(コンポーネントが画面に乗ったとき)
  useEffect(() => {
    console.log('App コンポーネントがマウントされた(最初の1回だけ)')
  }, [])

  // ④ count が変わるたびに実行
  useEffect(() => {
    console.log(`count が変わりました: ${count}`)
  }, [count])

  // ⑤ text が変わるたびに実行
  useEffect(() => {
    console.log(`text が変わりました: ${text}`)
  }, [text])

  return (
    <div style= padding: '24px', fontFamily: 'sans-serif' >
      <h1>React の基礎練習</h1>

      {/* カウンター */}
      <section style= marginBottom: '24px' >
        <h2>カウンター(useState + useEffect)</h2>
        <p>現在のカウント: {count}</p>
        <button onClick={() => setCount(count + 1)}>
          +1
        </button>
        <button onClick={() => setCount(0)} style= marginLeft: '8px' >
          リセット
        </button>
      </section>

      {/* テキスト入力 */}
      <section>
        <h2>テキスト入力(useState)</h2>
        <input
          type="text"
          value={text}
          onChange={(e) => setText([e.target](http://e.target).value)}
          placeholder="何か入力してみてください"
          style= padding: '4px 8px', width: '300px' 
        />
        <p>入力内容: {text}</p>
      </section>
    </div>
  )
}

export default App

これを入れて:

cd /var/www/django-5/frontend
npm run dev -- --host 0.0.0.0 --port 3000
  • ブラウザで `/` を開く
  • DevTools のコンソールを開くと、`useEffect` のログが流れるのが見えるはずです

6. ソースコードを読むときのコツ

コードを読むときは、だいたいこの順に追うと迷いにくいです:

  1. `useState(...)` が何個あるか見る→ どういう「状態」を持っているコンポーネントかを把握する
  2. `return (...)` の JSX を眺める→ どの状態が画面のどこに使われているかを見る
  3. `useEffect` を上から順に見る→ どのタイミングで何をしているか(初回? 値が変わったとき?)を確認する

コンポーネントに分割する

`App.jsx` には2つの独立した機能(カウンターとテキスト入力)が含まれています。これらを別々のコンポーネントに分割することで、コードの可読性と再利用性が向上します。

1. components フォルダを作成

プロジェクトの `src` フォルダ内に `components` フォルダを作成します。

src/
├── components/
│   ├── Counter.jsx
│   └── TextInput.jsx
├── App.jsx
└── main.jsx

2. Counter.jsx を作成

// src/components/Counter.jsx

import React, { useState, useEffect } from 'react'

function Counter() {
  const [count, setCount] = useState(0)

  useEffect(() => {
    console.log(`count が変わりました: ${count}`)
  }, [count])

  return (
    <section style={{marginBottom: '24px'}}>
      <h2>カウンター(useState + useEffect)</h2>
      <p>現在のカウント: {count}</p>
      <button onClick={() => setCount(count + 1)}>
        +1
      </button>
      <button onClick={() => setCount(0)} style={{marginLeft: '8px'}}>
        リセット
      </button>
    </section>
  )
}

export default Counter

3. TextInput.jsx を作成

// src/components/TextInput.jsx

import React, { useState, useEffect } from 'react'

function TextInput() {
  const [text, setText] = useState('')

  useEffect(() => {
    console.log(`text が変わりました: ${text}`)
  }, [text])

  return (
    <section>
      <h2>テキスト入力(useState)</h2>
      <input
        type="text"
        value={text}
        onChange={(e) => setText(e.target.value)}
        placeholder="何か入力してみてください"
        style={{padding: '4px 8px', width: '300px'}} 
      />
      <p>入力内容: {text}</p>
    </section>
  )
}

export default TextInput

4. App.jsx を簡潔に書き直す

// src/App.jsx

import React, { useEffect } from 'react'
import Counter from './components/Counter'
import TextInput from './components/TextInput'

function App() {
  useEffect(() => {
    console.log('App コンポーネントがマウントされた(最初の1回だけ)')
  }, [])

  return (
    <div style={{padding: '24px', fontFamily: 'sans-serif'}}>
      <h1>React の基礎練習</h1>
      <Counter />
      <TextInput />
    </div>
  )
}

export default App

Vite と CRA の書き出し場所の違い、 settings.py での設定

Django + React (Vite) の設定について

質問にあった `settings.py` の設定ですが、Vite を使う場合は少し注意が必要です。

問題点

  • `'DIRS': [os.path.join(BASE_DIR, 'frontend/build')]` という設定は、Create React App (CRA) の場合に使われる典型的なパターン
  • Vite の場合、ビルド後のファイルは `frontend/dist` に出力されるのがデフォルト
  • つまり `frontend/build` ではなく `frontend/dist` を指定する必要がある

正しい設定(Vite の場合)

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'frontend/dist')],  # ← build → dist に変更
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]

静的ファイルの設定も必要

さらに、Vite でビルドした JS/CSS ファイルを Django から配信するには、`settings.py` に以下も追加する必要があります:

STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'frontend/dist/assets'),
]

ビルドコマンド

Vite でビルドするときは:

cd frontend
npm run build
  • これで `frontend/dist` にファイルが生成される
  • Django 側はこの `dist` フォルダを見に行く設定にしておく

まとめ、Vite を使う場合:

  • `frontend/build` → `frontend/dist` に変更
  • `STATICFILES_DIRS` も `frontend/dist/assets` を追加
  • ビルドは `npm run build` で `dist` に出力される

Django + React (Vite) を特定のパス (/react/) で動かす方法

現在の状況:

  • `http://django-5.usual.tools/` で Django が動いている
  • `http://django-5.usual.tools/react/` で React (Vite) を動かしたい
  • それ以外のパスは Django のまま

Django のビューで React のエントリーポイントを配信

この方法では、Django が `/react/` にアクセスされたときに React アプリの `index.html` を返します。

urls.py の設定

# myproject/urls.py

from django.urls import path
from django.views.generic import TemplateView

urlpatterns = [
    # 既存の Django のルート
    path('admin/', admin.site.urls),
    # その他の Django のパス...
    
    # React アプリ用のパス
    # frontend/dist/index.html を指している。Vite でビルドされた React アプリのエントリーポイント
    path('react/', TemplateView.as_view(template_name='index.html'), name='react_app'),
]

Vite の設定を変更 (vite.config.js)

React アプリの静的アセットが `/static/` 配下を参照するように、ベースパスを設定します:

// vite.config.js

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
  base: '/static/',  // ← これを追加
  build: {
    outDir: 'dist',
  }
})

`base` オプションは、すべての静的アセット(JS、CSS、画像など)のベースパスを指定します。

  • base: '/react/' →/react/assets/index-xxx.js
  • base: '/static/' →/static/assets/index-xxx.js

React アプリをビルド

cd frontend
npm run build

まとめ

  • `vite.config.js` で `base: '/static/'` を設定(静的アセット)
  • `urls.py` で `path('react/', TemplateView.as_view(template_name='index.html'))` を追加
  • `npm run build` でビルドして `frontend/dist` に出力
  • `http://django-5.usual.tools/react/` でアクセス可能になる

React Vite のビルド出力先と静的ファイル配信の確認

問題の原因

`/react/assets/index-BdIFmBND.js` が 404 になっている理由は、Django が静的ファイル(JS/CSS)を正しく配信できていないからです。

1. Vite のビルド出力先を確認

Vite でビルドすると、デフォルトでは以下のように出力されます:

frontend/
├── dist/
│   ├── index.html
│   └── assets/
│       ├── index-BdIFmBND.js
│       └── index-xxxxxxxx.css (もしあれば)

つまり、`npm run build` を実行すると:

  • `frontend/dist/index.html` にエントリーポイント
  • `frontend/dist/assets/` に JS や CSS ファイル

2. Django の静的ファイル設定を確認

`settings.py` で以下の設定が必要です:

# settings.py

# 251227
STATIC_ROOT = '/var/www/django-5/staticfiles'

# 静的ファイルの設定(重要!)
STATIC_URL = '/react/'
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, 'frontend/dist'),  # ← ここを追加
]

4. 確認手順

  1. Vite でビルドを実行:
  2. ```bash
  3. cd frontend
  4. npm run build
  5. ```
  6. `frontend/dist/assets/` にファイルが生成されているか確認
  7. Django サーバーを再起動:
  8. ```bash
  9. python manage.py runserver
  10. ```
  11. `http://django-5.usual.tools/react/` にアクセス
  12. ブラウザの開発者ツール(F12)で Network タブを確認し、JS ファイルが正しく読み込まれているか確認

5. 本番環境の場合

本番環境では、`collectstatic` を使います:

python manage.py collectstatic

そして、Nginx や Apache で `/react/` を静的ファイルとして配信する設定を追加します。

まとめ

チェックリスト:

  • `npm run build` で `frontend/dist/assets/` にファイルが生成されているか
  • `settings.py` の `STATICFILES_DIRS` に `frontend/dist` が含まれているか
  • `urls.py` で `static()` を追加しているか(開発環境)
  • `STATIC_URL = '/react/'` になっているか