Record / Server Side
React Vite でチュートリアル
React Vite でチュートリアル
React プロジェクトを作る(Vite 版:軽くておすすめ)
cd /var/www/django-5
# Vite を使って React プロジェクト作成
# プロンプトが出たら:
# - Framework: React
# - Variant: JavaScript (でOK)
npm create vite@latest frontend
cd frontend
npm installnpm 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/32IPは通ったがエラー
下記でアクセスするとエラーがでました。
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の設定ファイルを編集して、外部ドメインからのアクセスを許可する必要があります。
- 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つ:
index.html- - ブラウザに最初に返される「土台のHTML」
- -
<div id="root"></div>が置いてあって、Reactはここに「入り込む」 src/main.jsx- - Reactアプリのエントリーポイント
- -
index.htmlの#rootに対して<App />を描画している src/App.jsx- - いま触る「メインのReactコンポーネント」
- - ここを書き換えると画面が変わる
package.json- - 使っているライブラリ一覧と、
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. ソースコードを読むときのコツ
コードを読むときは、だいたいこの順に追うと迷いにくいです:
useState(...)が何個あるか見る→ どういう「状態」を持っているコンポーネントかを把握するreturn (...)の JSX を眺める→ どの状態が画面のどこに使われているかを見るuseEffectを上から順に見る→ どのタイミングで何をしているか(初回? 値が変わったとき?)を確認する
コンポーネントに分割する
App.jsx には2つの独立した機能(カウンターとテキスト入力)が含まれています。これらを別々のコンポーネントに分割することで、コードの可読性と再利用性が向上します。
1. components フォルダを作成
プロジェクトの src フォルダ内に components フォルダを作成します。
src/
├── components/
│ ├── Counter.jsx
│ └── TextInput.jsx
├── App.jsx
└── main.jsx2. 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 Counter3. 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 TextInput4. 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 AppVite と 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. 確認手順
- Vite でビルドを実行:
- ```bash
- cd frontend
- npm run build
`frontend/dist/assets/にファイルが生成されているか確認- Django サーバーを再起動:
- ```bash
- python manage.py runserver
`http://django-5.usual.tools/react/にアクセス- ブラウザの開発者ツール(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/'になっているか