React on Rails:シンプルなアプリの構築
アプリケーションのフロントエンド側全体を構築する企業は、多くの場合、バックエンドを構築するためにRailsなどの同じフレームワークを選択します。長年にわたり、これは最良かつ最も信頼できるオプションでした。
今日、絶えず進化するフロントエンドユニバースにある多数のライブラリとフレームワークにより、開発者はバックエンドとフロントエンドの両方に異なるプラットフォームを選択し、それらを簡単に統合できます。
Reactはフロントエンドのパンゲアの巨人になりました。 Ruby on Railsを使用している場合は、デフォルトのRailsページをReactコード(またはその他のフロントフレームワーク)に切り替える必要があった可能性があります。おそらく、あなたはRails + Reactの機能が大好きで、両方の技術の力を1つのアプリに統合したいと思っているでしょう。
そして、それは大丈夫です!この記事は、実際の例、つまりビール製品の在庫を管理するCRUDアプリケーションを調査することを目的としています。最初はRailsで作成され、次にReactで作成された新しいフロントエンドがさらに統合されます。
ビールCRUDアプリケーション。
いくつかのステップで、Reactの主要部分、Railsがそれをどのように採用しているか、そしてRailsとReactの両方を将来のプロジェクトに統合する方法を理解します。
前提条件として、Ruby(およびRails)、Node.js、およびYarnを使用して環境をセットアップする必要があります。
できればnpmを選択してフロントパッケージを管理できますが、簡単にするためにYarnを使用します。
好みのフォルダで、次のコマンドを実行します:
rails new crud-rails-react
これにより、Railsプロジェクトが開始されます。次に、作成したプロジェクトをIDEで開き、Gemfileに直接移動します。 。
このコマンドがSQLitegemに対して生成するバグを修正する必要があります。したがって、必ずsqlite3
を見つけてください。 宝石とそれを次のように変更します:
gem 'sqlite3', '~> 1.3.10'
これにより、CRUDの例のデフォルトデータベースとしてSQLiteを使用するため、データベースのバージョン管理に関連する既知のエラーを防ぐことができます。
ただし、心配しないでください。他のデータベースでは、この問題は発生しません。
私は通常、ベースレイヤーからトップレイヤーまでを構築することを好むので、データベースモデルの作成を始めましょう。
必要なのは1つだけなので、Railsスキャフォールディングよりも優れたコマンド機能はありません。
rails g scaffold Beer brand:string style:string country:string quantity:integer & rake db:migrate
このモデルは非常に基本的なものなので、他の属性やタイプを自由に追加してください。
db / migrate内 フォルダには、「_ create_beers.rb」で終わる名前の新しいファイルがあります 」。これは、Railsがビールを表すために作成したレコードです。
次に、モデルは app / modelsの下に生成されます フォルダ。それらをそのままにして、次のコード行を db / seeds.rbに追加します。 ファイル:
Beer.create(brand: 'Double Stout', style: 'Stout', country: 'England', quantity: 54)
Beer.create(brand: 'Spaten', style: 'Helles', country: 'Germany', quantity: 3)
Beer.create(brand: 'Newcastle', style: 'Brown ale', country: 'UK', quantity: 12)
このファイルには、アプリの起動時にデータベースの初期データロードが保存されます。それらをデータベースに解放するには、次のコマンドを実行します。
rake db:seed
それでおしまい!これで、テーブルにビールがいくつかあります。 。
Webpackerのセットアップ
Webpackerは、JavaScriptアプリケーションで最も広く使用されている静的モジュールバンドラーの1つです。そのため、React機能を既存のアプリケーションに組み込むのに最適です。
Railsは、Rails内でJavaScriptのようなアプリケーションを管理するために完全に適合されたWebpackerバンドラーも提供します。
インストールするには、Gemfileに2行目を追加します 、次のように:
gem 'webpacker', '~> 4.3.x'
すごい!これは、アプリ開発全体で追加する必要がある唯一の宝石です。これが可能なのは、この記事の後半で設定するYarnにフロントの責任を委任しているためです。
次に、次のコマンドを発行してアップデートをインストールします。
bundle install
bundle exec rake webpacker:install
bundle exec rake webpacker:install:react
最初のものは、ほとんどのRails開発者によく知られています。 Webpacker自体を含むすべての依存関係をインストールするだけです。
Webpackerがインストールされると、Rakeを介してそのコマンドをエミュレートして、Webpackerの依存関係とReactの依存関係をインストールできます。
これは非常に重要なステップです。これは、WebpackerがすべてのJavaScript依存関係がRails環境に適切に設定されていることを確認するためです。したがって、npmやYarnで通常行うように、スキップしたり、直接実行したりしないでください。
コマンドが完了すると、いくつかのフォルダーとファイル(node_modules
など) およびpackage.json
)も作成されます。
これまでに行ったすべての設定は、CRUDアプリケーションがRailsでのみ機能するのに十分です。 rails s
を介してRailsサーバーを起動する場合 コマンド、これは結果になります:
自動生成されたビールCRUDをレールします。
ただし、Reactを使用した独自のCRUDが必要です。
まず、必要なすべてのフロントエンドの依存関係がYarnを介して構成されていることを確認する必要があります:
- 反応
- Reactアプリへのナビゲーションを処理するReactルーター
- すぐに使用できるReactコンポーネントのAntデザイン
Ant Design(antdとして知られている)は、エンタープライズレベルのアプリケーション用の豊富なオープンソースライブラリです。高度にカスタマイズ可能なReactコンポーネントを多数提供し、Webアプリの開発を大幅に簡素化します。
すべてをインストールするには、次のコマンドを実行します。
yarn add antd react-router-dom
react
を明示的に追加する必要はありません react-router-dom
以降のライブラリ します。
この時点で、 package.jsonを開くと ファイルの場合、これは自動生成されたコンテンツになります:
{
"dependencies": {
"@babel/preset-react": "^7.12.1",
"@rails/webpacker": "4.3.0",
"antd": "^4.7.2",
"babel-plugin-transform-react-remove-prop-types": "^0.4.24",
"prop-types": "^15.7.2",
"react": "^17.0.0",
"react-dom": "^17.0.0",
"react-router-dom": "^5.2.0"
},
"devDependencies": {
"webpack-dev-server": "^3.11.0"
}
}
これは、Rails-Reactコンボの最も基本的なセットアップです。したがって、コードに進むのは良いことです。
いくつかの重要なアクションは、前に進む前にRails側で対処する必要があります。
まず、ページリダイレクトを一元化するコントローラーを定義する必要があります。シングルページアプリケーション(SPA)を作成しているため、必要なコントローラーはBeersController
の1つだけです。 。
app / controllersの下で開きます フォルダを作成し、その内容を次のように変更します。
class BeersController < ApplicationController
def index
end
end
心配しないで;削除したすべてのコードは、次に作成されるコントローラーに配置されます。
このコントローラーの唯一の機能は、RailsからReactへの直接ルートを提供することです。そのため、index
のみを設定しています。 メソッド。
直接接続するには、 routers.rbを開きます。 configの下のファイル フォルダを作成し、その内容を次のように変更します:
Rails.application.routes.draw do
root 'beers#index'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
新しいroot
に注意してください 構成。はい、ルートのエンドポイントをbeers
にマッピングしています インデックス方式。
index.html.erbも空にする必要があります app / views / beers内のファイル RailsWebコンテンツをレンダリングしたくないのでフォルダ。これは、RailsにReactコードのみを表示させるために使用できるトリックです。
それでは、BeerAPIの作成に移りましょう。 API構造は、実質的にBeersController
と同じになります。 しかし、若干の変更があります。作成するには、次のコマンドを実行します。
rails generate controller api/v1/Beers
必ずバージョン管理システムを使用して作成してください。これにより、将来的にAPIを進化させ、通常のコントローラーと区別できるようになります。
次に、 app / controllers / api / v1 / beers_controller.rbを開きます。 ファイルを作成し、コードを次のように置き換えます。
class Api::V1::BeersController < ApplicationController
before_action :set_beer, only: [:show, :edit, :update, :destroy]
# GET /beers
# GET /beers.json
def index
@beers = Beer.all.order(brand: :asc)
render json: @beers
end
# GET /beers/1
# GET /beers/1.json
def show
if @beer
render json: @beer
else
render json: @beer.errors
end
end
# GET /beers/new
def new
@beer = Beer.new
end
# GET /beers/1/edit
def edit
end
# POST /beers
# POST /beers.json
def create
@beer = Beer.new(beer_params)
if @beer.save
render json: @beer
else
render json: @beer.errors
end
end
# PATCH/PUT /beers/1
# PATCH/PUT /beers/1.json
def update
end
# DELETE /beers/1
# DELETE /beers/1.json
def destroy
@beer.destroy
render json: { notice: 'Beer was successfully removed.' }
end
private
# Use callbacks to share common setup or constraints between actions.
def set_beer
@beer = Beer.find(params[:id])
end
# Only allow a list of trusted parameters through.
def beer_params
params.permit(:brand, :style, :country, :quantity)
end
end
ほとんどの操作は以前のコントローラーからリサイクルされました。
before_action
スニペットは、id
に従って適切なビールオブジェクトを復元します。 リクエスト内のパラメータ。 :only
の後に配列に配置された操作のみ 条項には、この自動回復機能が必要です。
残りのメソッドは、CRUDの各操作と同等です。リクエストへの応答として常にJSONを返すことを忘れないでください。これは、Reactコンポーネント内で使用する形式だからです。
最後に、 config / routers.rbを適応させる必要があります 新しく作成されたルートを再度含めます。したがって、ファイルの内容を次のように変更してください。
Rails.application.routes.draw do
namespace :api do
namespace :v1 do
get 'beers/index'
post 'beers/create'
delete 'beers/:id', to: 'beers#destroy'
end
end
root 'beers#index'
# For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
end
破棄ルートは、destroy
にマップする方法を示しています パスに明示的に設定されていない場合はメソッド。
Reactは、Webアプリケーションの構成要素のように機能するコンポーネントを介して機能します。それぞれが、コンテキストとして意味のある1つ以上のタスクを実行します。
つまり、コンポーネントは、プロパティをパラメータとして受け取り、その中でビジネスロジックを処理し、最終的に画面の一部を表す機能的なUIコンポーネントを返すことができるJavaScriptクラスまたは関数で構成されています。
作成する次の例から抽出した次のコードスニペットを使用します。
<Layout>
<Header />
<Content>...</Content>
<Footer>Honeybadger ©2020.</Footer>
</Layout>
はい、Reactコンポーネントは、HTMLによく似たJSX(JavaScript XML)と呼ばれるカスタムマークアップ言語を使用します。すべてのHTMLタグはJSXファイル内でも利用できます。 JSXの詳細については、こちらをご覧ください。
上記の例は、antdが構造レイアウトコンポーネントをどのように処理するかを示しています。コンポーネントは他のコンポーネントで構成され、互いに積み重ねられて全体を構成します。一部はプロパティを受け取り(オプションかどうかに関係なく)、一部は内部コンテンツを持つことができます。
コンポーネントは、クラスベースまたは関数ベースにすることができます。
クラスベースのコンポーネントは、以下に示すような通常のJavaScriptクラスとして作成されます。
class Beers extends React.Component {}
それらはReact.Component
から継承します クラスを作成し、ライフサイクルを設定し、初期化、レンダリング、破棄の各フェーズの前にコードを実行するための使用率メソッドを提供します。
ただし、最も重要な(そして必須の)メソッドはrender()
です。 、コンポーネントが更新されるたびに呼び出されます。
機能コンポーネントはES6の矢印機能を利用し、構文と複雑さの点でReactコンポーネントを簡素化します。
同じbeers
上記のコンポーネントは、次のような関数で表されます。
const Beers = () => <div>My Beers</div>;
これははるかに簡単ですよね?
Railsのインデックスページはすでに空になっています。次に、Reactをデフォルトのフロントエンドにする必要があることをRailsに通知します。
これを実現するには、次のコード行を<head>
に追加する必要があります。 app / views / layouts / application.html.erbのタグ ファイル:
<%= javascript_pack_tag 'index' %>
これにより、JavaScriptパックがアプリケーションヘッダーに追加され、Reactファイルを含むすべてのJavaScriptファイルがインデックス内で実行されます。 ページ。
index.jsx ファイルはインポートパックを指しているため、同じ名前になっています。
この目的のために、自動生成された app / javascript / packs / hello_react.jsxの名前を変更しましょう。 index.jsxへのファイル 。
次に、コードを次のように置き換えます。
import React from "react";
import { render } from "react-dom";
import App from "../components/App";
document.addEventListener("DOMContentLoaded", () => {
render(<App />, document.body.appendChild(document.createElement("div")));
});
このファイルをReactアプリケーションファイルと間違えないでください。これは、ReactDOMのrender
を介してReactアプリ階層全体をDOMにロードするファイルにすぎないためです。 機能。
通常、すべてのReactアプリケーションは index.jsから始まります React自体を含む必要なものすべてをロードするファイル。
App
タグは、階層の最上位コンポーネントをマップします。それでは、 index.jsxとして作成しましょう。 javascript / componentsの下 フォルダ(まだ存在しない場合は手動でフォルダを作成します)に次のコードを配置します:
import React from "react";
import Routes from "../routes/index";
import "antd/dist/antd.css";
export default () => <>{Routes}</>;
または、 index.jsx内にantdcSSファイルをインポートすることもできます。 。どちらのアプローチでも機能します。
ルートのリストはルートの下に配置されます フォルダ。それらはReactRouterライブラリから抽出されます。ReactRouterライブラリは私たちにとって大変な作業のほとんどを行います。これがその内容です:
import React from "react";
import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import Home from "../components/Home";
export default (
<Router>
<Switch>
<Route path="/" exact component={Home} />
</Switch>
</Router>
);
各ルートは、異なるRoute
内にマッピングする必要があります 鬼ごっこ。 path
パラメータは各ルートエンドポイントのURIと一致する必要がありますが、component
paramは、Reactルーターがリクエストをリダイレクトする必要があるコンポーネントを示します。
SPAのルートは1つであることを思い出してください。 / beers をマッピングする場合は、ここに他のパスを追加することもできます。 たとえば、ビールのリストについては、シンプルにしておきます。
また、Home
をインポートしていることに注意してください ここのコンポーネントはまだ存在していません。それでは、 Home.jsxとして作成しましょう。 コンポーネントの下 フォルダ。次に、次のコードを追加します。
import { Layout } from "antd";
import React from "react";
import Beers from "./Beers";
import Header from "./Header";
const { Content, Footer } = Layout;
export default () => (
<Layout className="layout">
<Header />
<Content style={{ padding: "0 50px" }}>
<div className="site-layout-content" style={{ margin: "100px auto" }}>
<h1>Beer Catalog</h1>
<Beers />
</div>
</Content>
<Footer style={{ textAlign: "center" }}>Honeybadger ©2020.</Footer>
</Layout>
);
Reactコンポーネントに関しては、上から下に構築することを好みます。このようにして、アプリを全体として機能させるために必要なすべてのコンポーネントを全体的に見ることができます。
Homeコンポーネントは、アセンブラのように動作します。 Layout
など、アプリの他のすべてのコンポーネントに対応します 、Header
、Content,
およびFooter
。
これらの各部分がどこから来ているのかを適切に区別することが重要です。 Antdは、画面を構成するためのレイアウト、コンテンツ、フッターなどの準備が整ったコンポーネントを多数提供しています。
これらは主にページパーツの構造に重点を置いていますが、CSSスタイルが組み込まれているものもあり、見栄えが良くなります。
Header.jsxコンポーネントファイル。これもjavascript/ components内に作成する必要があります。 フォルダは、ヘッダーのコンテンツを保持します。以下に示すように、シンプルなantdメニューとHoneybadgerのロゴが付いたdivが含まれています。
Antdメニュー項目。
以下に、 Header.jsxに配置するコードを示します。 :
import React from "react";
import { Layout, Menu } from "antd";
const { Header } = Layout;
export default () => (
<Header>
<div className="logo" />
<Menu theme="dark" mode="horizontal" defaultSelectedKeys={["1"]}>
<Menu.Item key="1">Home</Menu.Item>
<Menu.Item key="2">Our Services</Menu.Item>
<Menu.Item key="3">Contact</Menu.Item>
</Menu>
</Header>
);
Antd Menuコンポーネントは非常に使いやすいですが、利用可能なカスタマイズオプションの点で幅広いため、ナビゲーションドロワー、ドロップダウン、グループ、サブグループなどを作成できます。
defaultSelectedKeys
を提供していることに注意してください 、アクティブなアイテムをメニューに指示する配列。
メニューはどこにも移動しません。ルックアンドフィールのみを実現するために画面を占有します。それでは、beers
に移りましょう。 コンポーネント。
このコンポーネントは、ビールのリストと、削除、データのページ付け、テーブルの再読み込みなど、テーブル内で使用可能なアクションに焦点を当てています。
Reactのアクションとコンポーネントの視覚的表現。
上の画像を見てください。コンポーネントとアクションをより低いレベルに分類したので、ここで何が行われるかをよりよく理解できます。
Reactコンポーネントはstate
で構築されています 物体。このオブジェクトは、特定のコンポーネントに直接接続されたストアとして機能します。各コンポーネントには独自のstate
があります オブジェクトであり、状態を変更するたびに、コンポーネントが再レンダリングされます。
beers
の最初のアクション コンポーネントは、リストをテーブルに表示することです。この目的のために、このリストを配列で保持する必要があります:
state = {
beers: [],
};
この配列をフィードするには、前に作成したAPIコントローラーからリストを取得する必要があります。取得する関数を確認します:
loadBeers = () => {
const url = "api/v1/beers/index";
fetch(url)
.then((data) => {
if (data.ok) {
return data.json();
}
throw new Error("Network error.");
})
.then((data) => {
data.forEach((beer) => {
const newEl = {
key: beer.id,
id: beer.id,
brand: beer.brand,
style: beer.style,
country: beer.country,
quantity: beer.quantity,
};
this.setState((prevState) => ({
beers: [...prevState.beers, newEl],
}));
});
})
.catch((err) => message.error("Error: " + err));
};
わかりやすくするために、APIからデータをリクエストする必要があるたびに、最新のすべてのブラウザで利用できるFetchAPIを使用します。
上記の関数は、APIからビールの配列を取得するためにいくつかの手順を実行します。
- 最初に/indexをリクエストします エンドポイントを非同期にし、
then
応答ステータスがOKに等しいかどうかを確認します 。 - その場合、データをJSONとして返します。それ以外の場合は、
Error
をスローしましょう 。 Then
、結果の配列を反復処理して独自のビールオブジェクトを作成し、州のbeers
に追加します 配列。- プロセス中に問題が発生した場合は、
catch
ブロックは例外をキャプチャし、メッセージアラートとして表示します。
いいですね。これは、他のすべてのリクエストに対して行う手順とほぼ同じです。
しかし、antdはどのようにテーブルのデータを表示しますか?良い質問!次のコードを見てみましょう:
columns = [
{
title: "Brand",
dataIndex: "brand",
key: "brand",
},
...{
title: "",
key: "action",
render: (_text, record) => (
<Popconfirm title="Are you sure to delete this beer?" onConfirm={() => this.deleteBeer(record.id)} okText="Yes" cancelText="No">
<a href="#" type="danger">
Delete{" "}
</a>
</Popconfirm>
),
},
];
理解を深めるために少し簡略化しました。これは、テーブルのスケルトンを表す配列です。これがantdテーブルの仕組みです。テーブル構造(行と列)に関するメタデータ情報を配列として受け取る必要があります。
各列は配列内のオブジェクトであり、ここでは順序が重要です。 title
属性は列の名前を受け取り、dataIndex
名前は、Reactコンポーネント内でどのように認識されるか、およびkey
は一意の識別子です。
ほとんどの列の構成は、アクション列を除いて同様です。そこで、ユーザーがアイテムを削除したいときにトリガーするアクションのリンクを指定する必要があります。 antdのPopconfirmコンポーネントを使用していることに注意してください。
これは、アクションが発生する前にユーザーにアクションの確認を促す作業を容易にする非常に優れたコンポーネントです。下の画像はそれがどのように見えるかを示しています:
削除する前に確認ダイアログを表示します。
アイテムを削除するには、APIでの削除呼び出しとテーブルの再読み込みという2つの主要な操作を実行する必要があります。
削除機能は、最初に行ったフェッチと似ています:
deleteBeer = (id) => {
const url = `api/v1/beers/${id}`;
fetch(url, {
method: "delete",
})
.then((data) => {
if (data.ok) {
this.reloadBeers();
return data.json();
}
throw new Error("Network error.");
})
.catch((err) => message.error("Error: " + err));
};
見る?ここで新しいのは、HTTPmethod
だけです。 fetch
の2番目のパラメータとして渡されます 方法。さらに、then
内 句では、reloadBeers
と呼びます 関数。バックエンドからすべてのビールをもう一度再フェッチします。
この関数の内容はほとんど次のとおりです。
reloadBeers = () => {
this.setState({ beers: [] });
this.loadBeers();
};
州のbeers
をリセットします 配列を作成し、load関数を再度呼び出します。
最後に、antdタグを明示的に呼び出してコンポーネントを構成する必要があります。最終的なコンポーネントコードとどのように連携するかを見てみましょう:
import { Table, message, Popconfirm } from "antd";
import React from "react";
import AddBeerModal from "./AddBeerModal";
class Beers extends React.Component {
columns = [
{
title: "Brand",
dataIndex: "brand",
key: "brand",
},
{
title: "Style",
dataIndex: "style",
key: "style",
},
{
title: "Country",
dataIndex: "country",
key: "country",
},
{
title: "Quantity",
dataIndex: "quantity",
key: "quantity",
},
{
title: "",
key: "action",
render: (_text, record) => (
<Popconfirm title="Are you sure to delete this beer?" onConfirm={() => this.deleteBeer(record.id)} okText="Yes" cancelText="No">
<a href="#" type="danger">
Delete{" "}
</a>
</Popconfirm>
),
},
];
state = {
beers: [],
};
componentDidMount() {
this.loadBeers();
}
loadBeers = () => {
const url = "api/v1/beers/index";
fetch(url)
.then((data) => {
if (data.ok) {
return data.json();
}
throw new Error("Network error.");
})
.then((data) => {
data.forEach((beer) => {
const newEl = {
key: beer.id,
id: beer.id,
brand: beer.brand,
style: beer.style,
country: beer.country,
quantity: beer.quantity,
};
this.setState((prevState) => ({
beers: [...prevState.beers, newEl],
}));
});
})
.catch((err) => message.error("Error: " + err));
};
reloadBeers = () => {
this.setState({ beers: [] });
this.loadBeers();
};
deleteBeer = (id) => {
const url = `api/v1/beers/${id}`;
fetch(url, {
method: "delete",
})
.then((data) => {
if (data.ok) {
this.reloadBeers();
return data.json();
}
throw new Error("Network error.");
})
.catch((err) => message.error("Error: " + err));
};
render() {
return (
<>
<Table className="table-striped-rows" dataSource={this.state.beers} columns={this.columns} pagination={{ pageSize: 5 }} />
<AddBeerModal reloadBeers={this.reloadBeers} />
</>
);
}
}
export default Beers;
今、あなたはすべてを一緒に見ることができます。レンダリング関数は、そこにインポートしている2つのタグ(antdのTable
)を表示します。 コンポーネントとAddBeerModal
(数分で作成するモーダルフォーム)
テーブルコンポーネントは、pagination
を設定することで、結果を自動的にページ付けできるという点で非常に豊富です。 物体。ここで追加する唯一のプロパティは、各ページのサイズです(1ページあたり5つの結果)。
dataSource
属性は、バックエンドからマウントしたビールのリストとcolumns
を受け取ります 属性は、すでに作成したメタデータを受け取ります。
AddBeerModalコンポーネント
テーブルの下に、新しいビールを追加するためのボタンがあります。このボタンをクリックすると、以下に示すように、新しいビールをカタログに登録するためのフォームを含むモーダルが開きます。
カタログに新しいビールを追加します。
これは、antdがフォームを処理する方法を調べるのに最適な方法です。
まず、このコンポーネントで実行するアクションを分析してみましょう。コンポーネント自体は、ボタンとモーダルの2つで構成されていることに注意してください。
つまり、両方に関連する操作をマッピングする必要があります。
-
showModal
およびhandleCancel
モーダルの開始と終了に対処します。 -
onFinish
フォームを送信するとトリガーされます。
コンポーネントの状態を操作します。これにより、モーダルトグル(つまり、表示されているかどうか)のみが保存されます。
state = {
visible: false,
};
モーダルを表示または非表示にするには、このブール値を切り替える必要があります:
this.setState({
visible: true,
});
ビールのAPIを呼び出して新しいビールを登録するには、FetchAPIを再度利用する必要があります。
onFinish = (values) => {
const url = "api/v1/beers/";
fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(values),
})
.then((data) => {
if (data.ok) {
this.handleCancel();
return data.json();
}
throw new Error("Network error.");
})
.then(() => {
this.props.reloadBeers();
})
.catch((err) => console.error("Error: " + err));
};
サーバーにデータを送信するリクエストを呼び出すのはこれが初めてです。この場合、どのタイプの情報が進んでいるかをAPIに明示的に伝える必要もあります。そのため、headers
属性を通知する必要があります。
すべてがうまくいけば、モーダルを閉じてテーブルのリストをリロードするだけです。
それでは、コンポーネントのレンダリングとともに、すべてを一緒に見てみましょう。
import { Button, Form, Input, Modal, Select } from "antd";
import React from "react";
const { Option } = Select;
class AddBeerModal extends React.Component {
formRef = React.createRef();
state = {
visible: false,
};
onFinish = (values) => {
const url = "api/v1/beers/";
fetch(url, {
method: "post",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(values),
})
.then((data) => {
if (data.ok) {
this.handleCancel();
return data.json();
}
throw new Error("Network error.");
})
.then(() => {
this.props.reloadBeers();
})
.catch((err) => console.error("Error: " + err));
};
showModal = () => {
this.setState({
visible: true,
});
};
handleCancel = () => {
this.setState({
visible: false,
});
};
render() {
return (
<>
<Button type="primary" onClick={this.showModal}>
Create New +
</Button>
<Modal title="Add New Beer ..." visible={this.state.visible} onCancel={this.handleCancel} footer={null}>
<Form ref={this.formRef} layout="vertical" onFinish={this.onFinish}>
<Form.Item name="brand" label="Brand" rules={[{ required: true, message: "Please input your beer brand!" }]}>
<Input placeholder="Input your beer brand" />
</Form.Item>
<Form.Item name="style" label="Style" rules={[{ required: true, message: "Please input your beer style!" }]}>
<Input placeholder="Input your beer style" />
</Form.Item>
<Form.Item
name="country"
label="Country"
rules={[
{
required: true,
message: "Please input the country of the beer!",
},
]}
>
<Select showSearch placeholder="Select your beer country" optionFilterProp="children" style={{ width: "100%" }}>
<Option value="Finland">Finland</Option>
<Option value="Germany">Germany</Option>
<Option value="Netherlands">Netherlands</Option>
<Option value="UK">UK</Option>
<Option value="USA">USA</Option>
<Option value="Other">Other</Option>
</Select>
</Form.Item>
<Form.Item name="quantity" label="Quantity" rules={[{ required: true, message: "Please input the quantity!" }]}>
<Input type="number" placeholder="How many beers you desire?" />
</Form.Item>
<Form.Item>
<Button type="primary" htmlType="submit">
Submit
</Button>
</Form.Item>
</Form>
</Modal>
</>
);
}
}
export default AddBeerModal;
Antd allows us to specify each form’s item rules individually. If a field is required, just say so by providing a rules
attribute. You can customize the message it’ll display in case the user submits the form without filling it properly:
Validating form inputs.
Take a look at the Select
component, which translates a combo box. See how easy it is to create complex components by just providing the right attributes. For example, if you want to make your select searchable, just put the showSearch
property, there and it’s done:
Filtering results within a Select.
Antd will automatically filter the select options based on your input.
Styling
Sometimes, you’ll need to provide some CSS styling to components that do not provide a default (like antd’s table) or customize the ones that come built-in.
To do this, you can create as many CSS files as you want and organize them in a structure that pleases you. Rails already create an application.css file, under the app/assets/stylesheets folder. Open it and the following content:
.site-layout-content {
background: #fff;
padding: 24px;
min-height: 380px;
}
.logo {
width: 200px;
min-height: 31px;
margin: 16px 24px 16px 0;
float: left;
background-image: url(https://www.honeybadger.io/images/navbar_logo.svg?1602785015);
background-repeat: no-repeat;
}
.table-striped-rows th,
.table-striped-rows td {
border-bottom: 1px solid #dedddd !important;
}
.table-striped-rows tr:nth-child(2n) td {
background-color: #fbfbfb;
}
.table-striped-rows thead {
background-color: #f1f1f1;
}
Those are the CSS rules to make our table stripped, for example. Feel free to add as many extra styles here as you want.
Before heading to the tests, we need to disable the CSRF token checking that Rails automatically configures for our apps. To do so, go to the app/controllers/application_controller.rb file and change it to the following:
class ApplicationController < ActionController::Base
protect_from_forgery with: :null_session
end
This way, we avoid having to validate the tokens each time we perform a request.
すごい! Now, start your server via rails s
command, access the https://localhost:3000/ address, and play around with the CRUD.
As a homework task, I’d recommend that you try implementing the update functionality of the CRUD. You can adapt the edit
method at the API controller to receive the updated beer info and perform the update to the database. For the view, another modal would suit very well to accommodate the edit’s form.
You can also find the source code for this tutorial here. Good studies!
-
React on Rails:シンプルなアプリの構築
アプリケーションのフロントエンド側全体を構築する企業は、多くの場合、バックエンドを構築するためにRailsなどの同じフレームワークを選択します。長年にわたり、これは最良かつ最も信頼できるオプションでした。 今日、絶えず進化するフロントエンドユニバースにある多数のライブラリとフレームワークにより、開発者はバックエンドとフロントエンドの両方に異なるプラットフォームを選択し、それらを簡単に統合できます。 Reactはフロントエンドのパンゲアの巨人になりました。 Ruby on Railsを使用している場合は、デフォルトのRailsページをReactコード(またはその他のフロントフレームワーク)に
-
React Native アプリにビデオ通話を追加する方法
COVID-19 パンデミックの間、ビデオ通話は不可欠な日常活動になりました。チャット アプリ、音声通話、ビデオ通話などの機能を使用することで、友人や家族とのつながりを保つことができました。 それでは、ビデオ通話を可能にする独自の React Native アプリを作成しましょう。 このチュートリアルでは、Twilio プログラム可能なビデオ通話 API を使用して、React Native アプリにビデオ通話機能を実装する方法を学習します。 プロセスは非常に簡単です。ビデオ会議室を作成し、他のユーザーをその会議室に招待するだけです。これを行うには、カメラとマイクにアクセスする必要が