コンテンツへスキップ
Building a React Data Grid CRUD Admin App 30 Minutes

Building a React Data Grid CRUD Admin App 30 Minutes

要約:DR もし管理者用CRUDアプリを作ったことがあるなら、手順はご存知でしょう:ほぼ正しいグリッド、未完成のモーダルフォーム、「行編集は後で行う」付箋、そして3週間分の「日付欄でフィルターが機能しない」チケット。クラシックなNorthwind管理コンソールを30分以内に再構築しました — 完全なCRUD [...]

11 min read

TL;DR

もし管理者用CRUDアプリを作ったことがあるなら、手順はご存知でしょう。ほぼ正しいグリッド、未完成のモーダルフォーム、「行編集は後で行う」付箋、そして3週間分の「日付欄でフィルターが機能しない」チケット。

私は30分もかからずにクラシックなNorthwind管理コンソールを再構築しました。注文、顧客、商品、出荷者、営業担当者を横断する完全なCRUDを使い、UIにはIgnite UI React Data Grid、バックエンドには小さな.NET 10 + EF Core SQLite APIを使いました。合計UIコード:約1,500行のTypeScript。グリッドと戦う総時間:~0分。

実際の管理アプリ用のReactデータグリッドを評価する場合、重要なユースケースはこの点です。インライン編集、フィルタリング、リレーショナルドロップダウン、検証、マスターディテール、要約、テーマ設定など、ページが単なるおもちゃでなくなった後もすべて一つにまとまります。

Repo: react-grids/react-data-grids-crud

React Data Grid CRUDアプリで、発送者のドロップダウン付きインライン編集が表示されIgnite UI for React

これが、あなたに持っていってほしいスクリーンショットです。アクションストリップ→任意の行にカーソルを合わせると、編集と削除がスライドします。編集をクリックして→、行がインラインに編集可能になり、保存/キャンセルチップが表示されます。ドロップダウンから入力しながら検索できる組み合わせ→、ヘッダーをまとめて表示してください。行→を選択すると、その下にマスターディテールパネルが開き、結合データが表示されます。

それはカスタムビルドではありません。それは<IgrGrid rowEditable>プラス<IgrActionStrip><IgrGridEditingActions /></IgrActionStrip>です。コードは後で説明します。

CRUDのデモは簡単です。CRUDアプリはそうではありません。

社内管理アプリは至る所にあります。どの会社にもあります。デモリールには一度も入らない。それでも、行ごとに言えば、ユーザーは毎日使い、すべての粗削りな部分に気づくため、構築できるUIの中でも最も難しいものの一つです。

本物のCRUDページには以下が必要です:

  • ソート、フィルタリング、ページング(簡単そうに聞こえますが、日付の種類や地域をまたいで試してみてください)
  • 適切な保存/キャンセルの意味と楽観的なUXを用いたインライン行編集
  • リレーショナルルックアップが豊富な Create の対話形式
  • ユーザーが保存する前に実行される検証
  • マスターディテールで、ユーザーがクリックせずにコンテキストを確認できます
  • delete confirmation that doesn’t feel hostile
  • 読み込み/空/エラー状態は明確に伝えられます
  • キーボードのアクセシビリティ、フォーカス管理、スクリーンリーダーラベル

ほとんどのチームはそのうち60%を出荷し、勝利を宣言してオペレーションに肩をすくめて渡します。それがIgnite UI Reactデータグリッドが消し去るべき仕事です。

.NET Core、SQLiteでCRUDバックエンドを構築すること。

バックエンドは純粋な定型文です。5つのエンティティ、1つのEF Core DbContext、5つの小さなCRUDコントローラー、OpenAPI用のSwashbuckle:

// server/Program.cs
builder.Services.AddDbContext<AppDbContext>(opts =>
    opts.UseSqlite("Data Source=northwind.db"));
builder.Services.AddControllers();
builder.Services.AddSwaggerGen();

コントローラーは予想通りのものです:

// server/Controllers/ProductsController.cs
[ApiController, Route("api/[controller]")]
public class ProductsController(AppDbContext db) : ControllerBase
{
    [HttpGet]            public Task<List<Product>> GetAll()         => db.Products.AsNoTracking().ToListAsync();
    [HttpPost]           public async Task<Product> Create(Product p) { db.Add(p); await db.SaveChangesAsync(); return p; }
    [HttpPut("{id:int}")]public async Task Update(int id, Product p) { db.Update(p); await db.SaveChangesAsync(); }
    [HttpDelete("{id:int}")] public async Task Delete(int id)        { db.Remove(await db.Products.FindAsync(id)); await db.SaveChangesAsync(); }
}

dotnet runそして、Swagger UIが/swagger。今ではフロントエンドを書かずにすべてのエンドポイントを見て呼び出すことができます。これはReact側を簡単にするコントラクトです。フロントエンドは型付きREST APIで行ごとにミラーリングできます。

フロントエンドのサービス層はAPIをミラーリングします

// client/src/api/types.ts
export interface Product {
  productID: number;
  productName: string;
  unitPrice: number;
  stockLevel: number;
}

// client/src/api/services.ts
export const ProductsService = {
  list:   ()             => api.get<Product[]>('/api/Products'),
  get:    (id: number)   => api.get<Product>(`/api/Products/${id}`),
  create: (p: Omit<Product, 'productID'>) => api.post<Product>('/api/Products', p),
  update: (p: Product)   => api.put<void>(`/api/Products/${p.productID}`, p),
  remove: (id: number)   => api.del(`/api/Products/${id}`),
};

TypeScriptのタイプはデフォルトでJSON形状 ASP.NET シリアライズに一致しているため、マッピングレイヤーの維持は不要です。

この投稿全体が取り上げている部分: Reactデータグリッド

こちらが製品ページのCRUD配線全文です。

import {
  IgrGrid, IgrColumn, IgrPaginator,
  IgrActionStrip, IgrGridEditingActions,
} from 'igniteui-react-grids';

<IgrGrid
  data={filtered}
  primaryKey="productID"
  rowEditable={true}
  allowFiltering={true}
  filterMode="excelStyleFilter"
  moving={true}

  onRowEditDone={async (e: CustomEvent) => {
    const row = (e.detail as { newValue?: Product })?.newValue;
    if (!row) return;
    try {
      await ProductsService.update(row);
      toast.success(`Product "${row.productName}" saved.`);
      products.refetch();
    } catch (err) {
      toast.error(`Save failed: ${(err as Error).message}`);
    }
  }}

  onRowDeleted={async (e: CustomEvent) => {
    const row = (e.detail as { data?: Product })?.data;
    if (!row) return;
    try {
      await ProductsService.remove(row.productID);
      toast.success(`Deleted "${row.productName}".`);
      products.refetch();
    } catch (err) {
      toast.error(`Delete failed: ${(err as Error).message}`);
    }
  }}
>
  <IgrColumn field="productID"   header="ID"      editable={false} />
  <IgrColumn field="productName" header="Product" editable={true}  hasSummary />
  <IgrColumn field="unitPrice"   header="Price"
             dataType="number" editable={true}
             bodyTemplate={(c) => <span>{currency(c.cell.value)}</span>} />
  <IgrColumn field="stockLevel"  header="Stock"
             dataType="number" editable={true} hasSummary />

  <IgrPaginator perPage={10} />

  <IgrActionStrip>
    <IgrGridEditingActions addRow={false} />
  </IgrActionStrip>
</IgrGrid>

これにより、次の要素が得られます:

  • Sortable columns (click the header)
  • Excelスタイルのカラムフィルター(各ヘッダーのメニュー)
  • ページング(1ページあたり15/30/45ページ)フッターページネーター付き
  • ドラッグから並べ替えへの列
  • フッター内の数値および文字列の要約(平均、合計、カウント)
  • アクションストリップ→行をホバーし、編集/削除アイコンを表示します
  • 編集→をクリックするとセルが編集可能になり、保存/キャンセルチップが行の一番下に現れます
  • onRowEditDone統合された新しい行でファイア→APIにPUT
  • onRowDeletedユーザーが「削除」→「APIから削除」を確認すると発生します

私が挙げたいのはrowEditable={true}IgrActionStrip + IgrGridEditingActionsコンボの合計です。自分で編集状態を追跡するわけではありません。フラグも「汚れたセル」マップも、「ユーザーがEscapeを押したか」ハンドラーもisEditingありません。Reactデータグリッドが管理しています。コミットを聞いてサービスにプッシュするだけです。

もう一つは、好きなようにレンダリングできる機能です bodyTemplate。上記の例では通貨としてunitPriceフォーマットしています。同じフックで、ステータスピル、アバター、バッジ、色分けされたストックレベルなど、描画できるものならReact何でも描画できますが、基礎値のソートやフィルタリングを放棄せずに行えます。

リレーショナルルックアップは数値入力ではなくComboに含まれます

インライン編集はプリミティブには優れていますが、行に外部キー(salesPersonIDproductIDshipperID)がある場合、ユーザーにIDを入力したくはありません。検索可能なグループ化されたドロップダウンが欲しいです。

Enter IgrCombo:

<IgrCombo
  data={salespeople.map((s) => ({
    id: s.salesPersonID,
    name: fullName(s.salesPersonFirstName, s.salesPersonLastName),
    subtitle: s.salesPersonTitle ?? '',
  }))}
  valueKey="id"
  displayKey="name"
  groupKey="subtitle"     // ← grouped headers come for free
  singleSelect
  value={form.salesPersonID != null ? [form.salesPersonID] : []}
  onChange={(e) => {
    const arr = (e.detail as { newValue?: unknown[] }).newValue ?? [];
    setForm((f) => ({ ...f, salesPersonID: (arr[0] as number) ?? null }));
  }}
/>

ユーザーが気に入るエンタープライズ機能Ignite UI、タイプアドフィルタリング、チップレンダリングによるマルチセレクト、数千件のアイテムの仮想化、カスタムアイテムテンプレート用のスロットなどです。

組み合わせIgrComboIgrDatePickerANIgrInput Dを組み合わせれば、Ignite UI Reactコンポーネントで「リッチ・クリエイト・フォーム」というストーリーがすべてで管理されます。

テーマ設定 — インポート一回で完了

このアプリを作り始めたとき、私は単一のテーマ、組み込みのMaterial Lightテーマをファイルごとに1つのCSSインポートで適用していました:

// main.tsx
import 'igniteui-webcomponents/themes/light/material.css';
import 'igniteui-react-grids/grids/themes/light/material.css';

すべてのIgnite UI面—シェブロン、アクションストリップ、編集鉛筆、ページ内ボタン、ダイアログヘッダー—が自動的にテーマを拾います。アプリの進化に伴い、4つのデザインシステム(Material、Fluent、Indigo、Bootstrap)を基にテーマを交換できるセレクターを追加し、それぞれのダークバリアントも加えました。CSSインポートを1つ交換して、アプリ全体をリテーマ化します。

I also used the Ignite UI MCP servers

私はこれをIgnite UI MCPサーバーに対してコンポーネントとテーマ.すべてのページを一から手作業で配線するのではなく、プロンプトを使ってアプリ構造(フロントエンドとバックエンド)、コンポーネントの使用、ビジュアル設定を約約生成・洗練しました合計20分.

正確にどのように行われたか知りたい場合は、そのプロンプトはGitHubリポジトリのdocs/PROMPTS.mdにあります。私が使ったMCPのセットアップは、Ignite UI CLI MCPとテーマ付けMCPのワークフローです。

私が作らなくてよかったもの

比較を具体的にするために、私が書かなくてもよかったことを挙げます:

  • 仮想化されたテーブルレンダラー
  • 行編集の状態管理
  • ExcelスタイルのカラムフィルターUIです
  • マルチセレクトのグループコンボボックスで、タイプ先行機能を備えています
  • A date picker that renders a calendar
  • クロスブラウザのスクロール同期
  • Keyboard navigation across cells
  • 編集ダイアログのフォーカス管理
  • 適切な意味色を持つステータスピルレンダラー
  • Toast / snackbar plumbing
  • 上記のすべての作品に共通する一貫したビジュアルスタイル

それが価値のプロップです。Ignite UI Reactデータグリッドは、二度やりたくない作業の形状をまさにしています。

Try it

完全な情報源はgithub.com/react-grids/react-data-grids-crudにあります。クローンして.NET APIを実行し、Viteの開発サーバーを動かせば、2つのターミナルで動作するCRUDコンソールが完成します。

React Northwindの注文、営業担当者情報、Ignite UI for Reactを含むData Grid CRUDダッシュボード

React Data Gridを自分のアプリで使いたい場合は、まずはIgnite UI React Data Gridのページから始めて、そこから無料トライアルを受けてみてください。

YouTubeで動画をご覧ください:

ここで使ったグリッドはコマーシャルigniteui-react-gridsパッケージです。また、IgrGridLiteMITライセンスの無料版もありますigniteui-react/grid-lite行編集、マスターディテール、アクションストリップが不要なら。

FAQ

ReactでCRUD管理アプリを最速で構築する方法は何ですか?

最も早い方法は、Reactデータグリッドから始め、編集、フィルタリング、ページング、ルックアップ、検証、アクセシビリティなど難しい部分をすでに処理するコンポーネントをフォームすることです。このプロジェクトでは、Ignite UI Reactデータグリッドと小さなタイプ付きサービスレイヤーにより、通常CRUD作業を遅らせるカスタム配管の大部分が削除されました。

Reactデータグリッドとは何で、いつ必要ですか?

Reactデータグリッドは、インタラクティブでデータ量の高い画面向けに設計された高機能テーブルコンポーネントです。アプリが読み取り専用の行を超え、インライン編集、高度なフィルタリング、要約、キーボード操作、仮想化、リレーショナルデータワークフローが必要になったときに必要です。

インライン編集で本物のReact CRUDアプリを数分で作れるでしょうか?

はい、実は数分でできます。このデモでは、注文、顧客、商品、出荷者、営業担当者を含み、Ignite UI CLIとClaude CodeまたはGitHub Copilotを用いて30分以内にインラインの行編集、ダイアログ作成、確認削除、マスターディテール、リレーショナルドロップダウンを行せます。

なぜCRUDアプリにIgnite UI for Reactを使うのか?グリッドの挙動を手作業で構築するのではなく。

なぜなら、CRUDアプリのコストが高い部分は行のレンダリングではないからです。その行の周りのインタラクションモデル、編集ライフサイクル、フィルタリングUX、キーボードの挙動、グループ化された検索、要約、テーマ付け、一貫した状態遷移などです。Ignite UIこれらの動作を製品機能として提供しているため、コードはビジネスデータとAPI呼び出しに焦点を当て続けます。

Ignite UI for React.NET Web APIのバックエンドで動作しますか?

はい。この例では、フロントエンドが標準的なRESTエンドポイント上で.NET 10 + EF Core SQLite APIと通信します。クライアントコードはTypeScriptサービス関数の呼び出し/api/Products,/api/Ordersと他のCRUDルートで構成されています。

Ignite UI MCPサーバーでこれを構築したのですか?

はい。アプリはランタイムでIgnite UIコンポーネントを使用し、ビルドワークフローはIgnite UI CLI MCPとテーマ設定の恩恵も受けました。この組み合わせにより、AIツールがページの足場を組み、コンポーネントの質問に答え、テーマの決定をIgnite UIパターンに合わせるのに役立ちます。

Is Ignite UI React Data Grid free?

この投稿で使う完全なigniteui-react-gridsパッケージは商業的なものです。より軽いグリッドだけが必要な場合は、InfragisticsがMITライセンスの無料パッケージIgrGridLite提供igniteui-react/grid-liteただし、行編集やアクションストリップなどの高度なCRUD機能も商用版に含まれています。

本番のCRUDデータグリッドで最も重要な機能は何でしょうか?

基本はソート、フィルタリング、ページング、編集です。実際には、リレーショナルな検索、検証、削除確認、キーボードのアクセシビリティ、マスターディテールコンテキスト、読み込みや空状態、そしてページを追加しても崩れないテーマ設定も必要です。これらの特徴が、内部の工具が信頼性に感じられるか壊れやすいかを決める傾向があります。


Tags: #react #react-data-grid #typescript #crud #datagrid #ignite-ui #dotnet #admin-ui #mcp

デモを予約