created: 2026/01/04,
modified: 2026/01/04
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ファイルに記載されているすべての依存パッケージがインストールされます。
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でサーバーが正常に稼働しています。
プロセスが残っている場合
# プロセス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
自分のPCのIPアドレス(`20.234.123.56`)のみを許可する場合:
20.234.123.56/32
重要: 末尾に`/32`を付けることで、そのIPアドレス1つだけを指定できます。
複数の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
下記でアクセスするとエラーがでました。
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の設定ファイルを編集して、外部ドメインからのアクセスを許可する必要があります。
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'
]
}
})
開発サーバーを再起動しなくてもいけました
src/App.jsx の拡張子 jsx は何者?react 専用?
JSX とは?
JSX(JavaScript XML)は、JavaScriptの構文拡張です。HTMLのような構文でUIを記述できるようにするものです。
React 専用?
JSXは主にReactで使用されますが、React専用ではありません。他のライブラリやフレームワーク(例:Preact、Solid.js)でも使用できます。
JSX の特徴
.jsx と .js の違い
技術的には`.js`ファイルでも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へようこそ')
);
}
{/* カウンター */}
はどういうこと?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>
);
}
まとめ
`useState`と`useEffect`はReactのフック (Hooks)で、`useState`はコンポーネントの状態(データ)を管理し、`useEffect`は副作用(データ取得、DOM操作、タイマー設定など)を実行します。`useState`で状態を更新すると、その変更をトリガーに`useEffect`が実行され、UIの更新と同期的な処理を連携させることがReactの基本的な動作パターンです。
`/var/www/django-5/frontend` の中身はだいたいこんな感じです:
frontend/
├── index.html
├── vite.config.js
├── package.json
└── src/
├── main.jsx
├── App.jsx
├── assets/
└── ...
よく見るのはこの4つ:
最低限ここだけ押さえておけばOKです:
<body>
<div id="root"></div>
<script type="module" src="/src/main.jsx"></script>
</body>
---
よくある形はこんな感じです(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>,
)
やっていること:
最初はこんな感じのコードが入っているはずです(多少違っても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` です。
次でここを分解します。
const [count, setCount] = useState(0)
「`count` という状態と、それを書き換える関数 `setCount` を用意して、最初の値は 0 にしておいて」
let count = 0
function App() {
count = count + 1 // ← これをやっても画面は勝手に更新されない
}
`useState` を使うと
ポイント:
まず、よく見るテンプレート:
import { useState, useEffect } from 'react'
function App() {
const [count, setCount] = useState(0)
useEffect(() => {
console.log('count が変わったタイミング or 初回レンダーで実行')
}, [count])
return (
<div>...</div>
)
}
「レンダリングのあとに『副作用(サイドエフェクト)』を実行するためのフック」
副作用の例:
useEffect(() => {
console.log('毎回レンダー後に実行される')
})
useEffect(() => {
console.log('最初の1回だけ実行')
}, [])
useEffect(() => {
console.log(`count が ${count} に変わったときに実行`)
}, [count])
`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
コードを読むときは、だいたいこの順に追うと迷いにくいです:
`App.jsx` には2つの独立した機能(カウンターとテキスト入力)が含まれています。これらを別々のコンポーネントに分割することで、コードの可読性と再利用性が向上します。
プロジェクトの `src` フォルダ内に `components` フォルダを作成します。
src/
├── components/
│ ├── Counter.jsx
│ └── TextInput.jsx
├── App.jsx
└── main.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
// 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
// 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
質問にあった `settings.py` の設定ですが、Vite を使う場合は少し注意が必要です。
問題点
正しい設定(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
まとめ、Vite を使う場合:
現在の状況:
この方法では、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、画像など)のベースパスを指定します。
React アプリをビルド
cd frontend
npm run build
まとめ
`/react/assets/index-BdIFmBND.js` が 404 になっている理由は、Django が静的ファイル(JS/CSS)を正しく配信できていないからです。
Vite でビルドすると、デフォルトでは以下のように出力されます:
frontend/
├── dist/
│ ├── index.html
│ └── assets/
│ ├── index-BdIFmBND.js
│ └── index-xxxxxxxx.css (もしあれば)
つまり、`npm run build` を実行すると:
`settings.py` で以下の設定が必要です:
# settings.py
# 251227
STATIC_ROOT = '/var/www/django-5/staticfiles'
# 静的ファイルの設定(重要!)
STATIC_URL = '/react/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'frontend/dist'), # ← ここを追加
]
本番環境では、`collectstatic` を使います:
python manage.py collectstatic
そして、Nginx や Apache で `/react/` を静的ファイルとして配信する設定を追加します。
チェックリスト: