mcpField
R
reflex-dev

reflex-dev

github.com/silvainfm開発3106 回インストール14 週間前に更新
リポジトリ

reflex-dev について

reflex-devは、JavaScriptを書かずにReflexとPythonでフルスタックWebアプリを構築するための開発スキルです。状態管理、コンポーネント、イベント処理、ルーティング、スタイル設定、開発手順を扱います。

主な機能

  1. 01Pythonでアプリを構築
  2. 02サーバー側の状態管理
  3. 03コンポーネントによるUI構成
  4. 04ユーザーイベントの処理
  5. 05ルートとレスポンシブレイアウトの設定
  6. 06アプリの実行とエクスポート

活用シーン

  1. 01インタラクティブなWebページの作成
  2. 02フォームやデータテーブルの構築
  3. 03アップロードとリアルタイム状態更新の処理

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/silvainfm/claude-skills --skill reflex-dev

インストール後、Claude Code で/reflex-devを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

examples/
counter_app.py
data_table_crud.py
file_upload.py
form_validation.py
README.md
references/
patterns.md

詳細

Reflex 開発

概要

Reflex は、JavaScript を記述せずに Web アプリケーションを構築できるフルスタック Python フレームワークです。アプリは React フロントエンドと FastAPI バックエンドにコンパイルされ、状態管理とイベントハンドラーはすべて Python で実行されます。

アーキテクチャ:

  • フロントエンド:UI の描画用に React(JavaScript)へコンパイルされます
  • バックエンド:Python のイベントハンドラーを実行する FastAPI サーバー
  • 通信:リアルタイムの状態更新に WebSocket を使用します
  • 状態:サーバー側の Python 状態がフロントエンドと同期されます

コアコンセプト

状態管理

状態は、変更可能なすべてのデータとイベントハンドラーを保持する Python クラスです。すべての状態変数は JSON でシリアライズ可能である必要があります。

import reflex as rx

class AppState(rx.State):
    # State variables (any JSON-serializable type)
    count: int = 0
    items: list[str] = []
    user_name: str = ""

    # Event handlers - the ONLY way to modify state
    def increment(self):
        self.count += 1

    def add_item(self, item: str):
        self.items.append(item)

    # Computed vars (derived state)
    @rx.var
    def item_count(self) -> int:
        return len(self.items)

主なルール:

  • 状態変数は JSON でシリアライズ可能(int、str、list、dict、bool、float)でなければなりません
  • 状態を変更できるのはイベントハンドラーだけです
  • 計算値/派生値には @rx.var デコレーターを使用します
  • 状態はユーザーセッションごとに保持されます(ユーザー間で分離されます)
コンポーネント

コンポーネントは UI を構築するための基本要素です。Reflex には 60 種類以上の組み込みコンポーネントが用意されています。

import reflex as rx

def header() -> rx.Component:
    return rx.heading("My App", size="lg")

def counter_component(state: AppState) -> rx.Component:
    return rx.vstack(
        rx.text(f"Count: {state.count}"),
        rx.button("Increment", on_click=state.increment),
        spacing="4"
    )

一般的なコンポーネント:

  • レイアウト:rx.vstack、rx.hstack、rx.box、rx.container
  • テキスト:rx.heading、rx.text、rx.code
  • 入力:rx.input、rx.text_area、rx.select、rx.checkbox
  • インタラクティブ要素:rx.button、rx.link、rx.icon_button
  • データ:rx.table、rx.data_table、rx.list
  • グラフ:rx.recharts.line_chart、rx.recharts.bar_chart など
イベントハンドラー

イベントハンドラーはユーザーの操作に応答し、状態を変更する唯一の方法です。

class FormState(rx.State):
    form_data: dict[str, str] = {}

    # Simple event handler
    def handle_submit(self):
        print(f"Submitted: {self.form_data}")

    # Event handler with argument
    def update_field(self, field: str, value: str):
        self.form_data[field] = value

    # Async event handler (for API calls, DB queries)
    async def fetch_data(self):
        # Can use any Python library
        import httpx
        async with httpx.AsyncClient() as client:
            response = await client.get("https://api.example.com/data")
            self.data = response.json()

イベントトリガー(コンポーネントとハンドラーを接続します):

  • on_click:ボタンのクリック
  • on_change:入力フィールドの変更
  • on_submit:フォームの送信
  • on_mount:コンポーネントの初回描画
  • on_blur、on_focus:入力フィールドのフォーカスイベント

プロジェクト構成

Reflex アプリの標準的な構成:

my_app/
├── my_app/
│   ├── __init__.py         # Empty
│   └── my_app.py           # Main app file (State + pages)
├── assets/                 # Static files (images, fonts, etc.)
├── .web/                   # Auto-generated frontend (don't edit)
├── rxconfig.py             # Reflex configuration
└── requirements.txt        # Python dependencies
メインアプリファイルのパターン
import reflex as rx

# 1. Define State
class State(rx.State):
    count: int = 0

    def increment(self):
        self.count += 1

# 2. Define Pages
def index() -> rx.Component:
    return rx.container(
        rx.heading("Welcome"),
        rx.button("Click", on_click=State.increment),
        rx.text(f"Count: {State.count}")
    )

def about() -> rx.Component:
    return rx.container(
        rx.heading("About"),
        rx.link("Home", href="/")
    )

# 3. Create App and Add Routes
app = rx.App()
app.add_page(index, route="/")
app.add_page(about, route="/about")

一般的なパターン

フォーム処理
class FormState(rx.State):
    name: str = ""
    email: str = ""

    def handle_submit(self, form_data: dict):
        self.name = form_data.get("name", "")
        self.email = form_data.get("email", "")

def form_page():
    return rx.form(
        rx.vstack(
            rx.input(name="name", placeholder="Name"),
            rx.input(name="email", placeholder="Email"),
            rx.button("Submit", type="submit"),
        ),
        on_submit=FormState.handle_submit,
    )
データテーブル
class DataState(rx.State):
    data: list[dict] = [
        {"id": 1, "name": "Alice", "age": 25},
        {"id": 2, "name": "Bob", "age": 30},
    ]

def data_table_page():
    return rx.data_table(
        data=DataState.data,
        columns=["id", "name", "age"],
        sort=True,
        search=True,
        pagination=True,
    )
ファイルアップロード
class UploadState(rx.State):
    async def handle_upload(self, files: list[rx.UploadFile]):
        for file in files:
            upload_data = await file.read()
            # Process file data
            outfile = f"./uploads/{file.filename}"
            with open(outfile, "wb") as f:
                f.write(upload_data)

def upload_page():
    return rx.vstack(
        rx.upload(
            rx.button("Select Files"),
            id="upload1",
        ),
        rx.button(
            "Upload",
            on_click=UploadState.handle_upload(rx.upload_files(upload_id="upload1"))
        ),
    )
データベース統合(DuckDB を使用)
import duckdb
import polars as pl

class DBState(rx.State):
    records: list[dict] = []

    async def load_data(self):
        # Use existing database connection
        conn = duckdb.connect("data/mydb.duckdb")
        df = conn.execute("SELECT * FROM mytable").pl()
        self.records = df.to_dicts()
        conn.close()

    async def insert_record(self, data: dict):
        conn = duckdb.connect("data/mydb.duckdb")
        conn.execute(
            "INSERT INTO mytable (name, value) VALUES (?, ?)",
            [data["name"], data["value"]]
        )
        conn.close()
        await self.load_data()  # Refresh

スタイリングとレイアウト

インラインスタイリング
rx.box(
    rx.text("Styled text"),
    bg="#1a5f9e",
    color="white",
    padding="4",
    border_radius="md",
)
レスポンシブレイアウト
rx.container(
    rx.responsive_grid(
        rx.box("Item 1", bg="blue"),
        rx.box("Item 2", bg="green"),
        rx.box("Item 3", bg="red"),
        columns=[1, 2, 3],  # 1 col mobile, 2 tablet, 3 desktop
        spacing="4",
    ),
    max_width="1200px",
)
一般的なスタイルプロパティ
  • レイアウト:width、height、padding、margin、display
  • 色:bg(背景)、color(テキスト)
  • タイポグラフィ:font_size、font_weight、text_align
  • 境界線:border、border_radius、border_color
  • 間隔:spacing(スタック用)、gap

ルーティング

複数ページ
app = rx.App()

# Route with parameters
@rx.page(route="/user/[id]")
def user_page() -> rx.Component:
    return rx.text(f"User ID: {State.router.page.params.get('id')}")

# Simple routes
app.add_page(index, route="/")
app.add_page(about, route="/about")
ナビゲーション
# Links
rx.link("Go to About", href="/about")

# Programmatic navigation
def go_home(self):
    return rx.redirect("/")

開発ワークフロー

新しいアプリを初期化する
pip install reflex
reflex init
開発サーバーを起動する
reflex run

アプリは http://localhost:3000 で起動し、自動で再読み込みされます。

一般的なコマンド
reflex run          # Start dev server
reflex export       # Build production bundle
reflex db init      # Initialize database (if using Reflex DB)
reflex db migrate   # Run migrations

ベストプラクティス

  1. 状態の整理:大きな状態をサブ状態に分割します

    class AuthState(rx.State):
        user: str = ""
    
    class DataState(rx.State):
        items: list = []
    
  2. コンポーネントの再利用性:再利用可能なコンポーネント関数を作成します

    def card(title: str, content: str) -> rx.Component:
        return rx.box(
            rx.heading(title, size="md"),
            rx.text(content),
            padding="4",
            border="1px solid #ddd",
        )
    
  3. イベントハンドラーのパフォーマンス:I/O 操作には async を使用します

    async def fetch_data(self):
        # Async I/O won't block other users
        self.data = await some_api_call()
    
  4. 型ヒント:状態変数とイベントハンドラーには必ず型ヒントを付けます

    count: int = 0
    items: list[str] = []
    
    def update_count(self, value: int) -> None:
        self.count = value
    

参考資料

ドキュメント
サンプルアプリ

動作する完全なサンプルについては、examples/ ディレクトリを参照してください:

  • シンプルなカウンターアプリ
  • CRUD 操作に対応したデータテーブル
  • バリデーション付きフォーム
  • ファイルのアップロードと処理
一般的なパターンのリファレンス

詳細な例については、references/patterns.md を参照してください:

  • 認証フロー
  • リアルタイム更新
  • 複雑なフォームのバリデーション
  • 複数ステップのワークフロー
  • グラフを使ったデータの可視化

コメント