React.js-Rails開発者向けガイド

エントリレベルでは、このような翻訳はRailsコミュニティの発展に貢献しています。

さらにテキストでは、斜体で示されているすべてのもの、私のコメント(多くはありません)



画像



React.jsの概要



React.jsはJavaScriptフレームワークチームの新しい人気者であり、そのシンプルさが際立っています。 他のフレームワークが完全なMVC (Model View Controller)アプローチを実装する場合、ビュー(ディスプレイ)(事実-一部の人々はこれらのフレームワークのディスプレイ(V)の一部をReactで書き換える)のみを実装するようにReactに指示できます。



Reactアプリケーションは、コンポーネントとステータスの2つの基本原則に基づいています。 コンポーネントは、組み込みまたはカスタムの小さなコンポーネントで構成できます。 Facebookのスタッフが一方向のリアクティブデータストリームを呼び出すことは、インターフェイス(UI)がすべての状態変化に対応することを意味します。



React.jsの優れた機能の1つは、追加の依存関係を必要としないことです。これにより、jsライブラリとの接続が保証されます。 これを使用して、外部インターフェイスを作成するためにRailsスタックに含めるか、「ステロイドのレール」を作成するように言うことができます。



アプリケーションコストトラッキングレイアウト





このガイドでは、小さなアプリケーションをゼロから作成して、アクションを追跡します。 各レコード( 以降、Recordと同じ )は、日付、名前、および金額で構成されます。 エントリは、金額がゼロより大きい場合はクレジットと見なされ、それ以外の場合はクレジットと見なされます。 プロジェクトのレイアウトは次のとおりです。



画像



合計すると、アプリケーションは次のように動作します。

  1. ユーザーが水平形式で新しいレコードを作成すると、レコードテーブルに挿入されます
  2. ユーザーは既存の投稿を編集できます。
  3. [削除]ボタンをクリックすると、テーブルから関連付けが削除されます。
  4. 既存のレコードを追加、編集、または削除すると、ページ上部のボックスの量が更新されます




RailsプロジェクトでReact.jsを初期化する





まず、新しいプロジェクトを作成する必要があります。これをAccountsと呼びましょう

rails new accounts
      
      





このプロジェクトのユーザーインターフェイスには、 bootstrap



を使用します。 インストールプロセスはこの記事の範囲外ですが、 gitリポジトリからの指示を使用して公式のbootstrap-sass



をインストールできます



これでプロジェクトがインストールされました。 React



接続を継続します。 このガイドでは、このgemのクールな機能を使用するため、公式のgemから接続することにしましたが、Railsを使用して目標を達成する別の方法があるか、公式ページからソースコードをダウンロードしてjavascripts



フォルダーに貼り付けることができます。



Railsアプリケーションを開発している場合は、gemのインストールがいかに簡単かを知っています: Gemfile



へのreact-rails



追加

  gem 'react-rails', '~> 1.0'
      
      





次に、レールに新しいgemをインストールするように親切に伝えます

 bundle install
      
      





React-railsには、 Reactコンポーネントが存在するapp/assets/javascripts



フォルダー内にcomponents.js



ファイルを作成するスクリプトがインストールされています。

 rails g react:install
      
      





インストールを開始した後にapplication.js



ファイルを見ると、3つの新しい行が表示されます。

 //= require react //= require react_ujs //= require components
      
      





基本的に、これにはReactライブラリが含まれ、コンポーネントおよび類似のファイルはujs



保存されujs



ファイル名から推測できるように、 react-rails



はReactコンポーネントのインストールに役立ち、Turbolinksイベントも処理する控えめなJSドライバーが含まれています。



リソース作成





Record



リソースを作成します。これは、タイトル(タイトル)の日付(日付)と金額(金額)で構成されます。

scaffold



生成を使用する代わりに、 resource



ジェネレータを使用します。

scaffoldジェネレータを使用して作成されたすべてのファイルとメソッドを使用するわけではありません。 そうしないと、scaffoldを起動して未使用のファイル/メソッドを削除することが可能になりますが、この場合のプロジェクトは少し汚いでしょう。 その後、プロジェクト内で次のコマンドを実行します。

 rails g resource Record title date:date amount:float
      
      





この魔法の後、新しいモデル(モデル)コントローラー(コントローラー)とルート(ルート)ができました。 次に、データベースを作成し、移行を実行します。

 rake db:create db:migrate
      
      





さらに、次の方法でいくつかのレコードを作成できます。

 rails console Record.create title: 'Record 1', date: Date.today, amount: 500 Record.create title: 'Record 2', date: Date.today, amount: -100
      
      





サーバーを起動することを忘れないでください

 rails s
      
      





できた! コードを書くことができます。



ネストされたコンポーネント:レコードリスト





最初のタスクでは、作成したレコードをテーブル内にレンダリングする必要があります。

最初に、 RecordsController



内にindex



アクションを作成する必要があります。

 # app/controllers/records_controller.rb class RecordsController < ApplicationController def index @records = Record.all end end
      
      





ここで、 apps/views/records/



に新しいindex.html.erb



ファイルを作成する必要があります。このファイルは、RailsアプリケーションとReactコンポーネント間のブリッジになります。 このタスクを実行するには、ヘルパーメソッドreact_component



を使用します。このメソッドは、Reactという名前を取得します。これは、渡すデータと共にレンダリングするコンポーネントです。

 <%# app/views/records/index.html.erb %> <%= react_component 'Records', { data: @records } %>
      
      





このヘルパーはreact-rails gemによって提供されることに注意してください。別の統合Reactメソッドを使用することにした場合、このヘルパーは使用できません。



これで、 localhost:3000/records



アクセスできます。 明らかに、何かが正しく機能していません。これはすべて、レコード(Reactコンポーネント)が欠落しているためです。 しかし、生成されたHTMLをブラウザー内に取り込むと、このようなものを挿入できます。

 <div data-react-class="Records" data-react-props="{...}"> </div>
      
      





このマークアップにより、 react_ujs



はReactコンポーネントをレンダリングしようとしているかどうかを判断し、 react_ujs



を介して送信する設定を含むインスタンスを作成します。この場合、コンテンツは@records







最初のコンポーネントを作成し、 javascripts/components



ディレクトリ内に新しいファイルrecords.js.coffee



作成します。このファイルにはRecords



コンポーネントが含まれます。

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass render: -> React.DOM.div className: 'records' React.DOM.h2 className: 'title' 'Records'
      
      





各コンポーネントには、コンポーネントのレンダリングを変更するrenderメソッドが必要ですReactComponent



メソッドは、 ReactComponent



クラスのインスタンスを返す必要があります。そのため、Reactが再レンダリングを実装すると、最適に実行されます。



発言。 別のケースでは、メソッドのレンダー内のReactComponents



インスタンスは、JSX構文を使用して記述できます。

上記のコードと同等:

  render: -> `<div className="records"> <h2 className="title"> Records </h2> </div>`
      
      





個人的には、CoffeeScriptを使用する場合、コードはHAMLのように階層構造に変換されるため、 React.DOM



構文を使用することを好みます。既存のERBコードをJSXに変換します。



ブラウザを更新



画像



素晴らしい。 最初のReactコンポーネントをレンダリングしました。 次に、メモを表示します。



さらに、Reactコンポーネントのrenderメソッドは、再レンダリングが必要かどうかを理解するために、設定を使用して他のコンポーネントや状態と交換することに依存しています。 コンポーネントの状態とプロパティを必要な値で初期化する必要があります。

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass getInitialState: -> records: @props.data getDefaultProps: -> records: [] render: -> ...
      
      





getDefaultProps



メソッドは、データの転送を忘れた場合、インスタンス化するときにコンポーネントの設定を初期化し、 getInitialState



メソッドはコンポーネントの初期状態を生成します。 通常、Railsビューを使用してレコードを表示する必要があります。



行数をフォーマットするためのヘルパーメソッドが必要なようで、行の簡単なフォーマットを挿入して、すべてのcoffee



ファイルで使用できるようにしますutils.js.coffee



次の内容の新しいutils.js.coffee



ファイルを作成します。

 # app/assets/javascripts/utils.js.coffee @amountFormat = (amount) -> '$ ' + Number(amount).toLocaleString()
      
      





新しいRecordコンポーネントを作成し、個々のレコードを表示し、 javascripts/components



ディレクトリに新しいrecord.js.coffee



ファイルを作成して、次のコードを貼り付ける必要があります。

 # app/assets/javascripts/components/record.js.coffee @Record = React.createClass render: -> React.DOM.tr null, React.DOM.td null, @props.record.date React.DOM.td null, @props.record.title React.DOM.td null, amountFormat(@props.record.amount)
      
      





Recordコンポーネントは、レコードの各属性のセルを含むテーブルの列に表示されます。 React.DOM.*



これらのReact.DOM.*



をカウントする心配はありませんReact.DOM.*



呼び出し、つまり、コンポーネントに属性を渡さないことを意味しますReact.DOM.*



、次のコードでコンポーネントのレコード内のrenderメソッドを更新します。

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass ... render: -> React.DOM.div className: 'records' React.DOM.h2 className: 'title' 'Records' React.DOM.table className: 'table table-bordered' React.DOM.thead null, React.DOM.tr null, React.DOM.th null, 'Date' React.DOM.th null, 'Title' React.DOM.th null, 'Amount' React.DOM.tbody null, for record in @state.records React.createElement Record, key: record.id, record: record
      
      





何が起こったのか見ましたか? 内部にヘッダーと本文を含むテーブルを作成しました。 既存の各レコードに対してRecord要素を作成しました。 言い換えると、ビルトイン/カスタムReactコンポーネント、Coolをネストしています。 そう?



動的な相続人(この場合はレコード)がある場合、要素を動的に生成するための構成キーを提供する必要があります。したがって、ReactがUI(ユーザーインターフェイス)を更新する時間はあまりありません。

キー:Record要素を作成するときに、実際のレコードと一緒にrecord.id



。 これを行わない場合、ブラウザーのJSコンソールで警告を取得する必要があります(おそらく近い将来、頭痛の種になることもあります)。



画像



ここでこのセクションのコードを見るか、セクションの変更を見ることができます



親と子の関係:レコードの作成





作成されたすべてのレコードを表示し、新しいレコードを作成するフォームを含めることもできます。 この機能をReact / Railsアプリケーションに追加しましょう。 まず、コントローラーのcreate method



を追加する必要がcreate method



ます( _strongparams



使用を忘れないでください)

 class RecordsController < ApplicationController ... def create @record = Record.new(record_params) if @record.save render json: @record else render json: @record.errors, status: :unprocessable_entity end end private def record_params params.require(:record).permit(:title, :amount, :date) end end
      
      





次に、Reactコンポーネントを作成し、新しいレコードの作成を追跡する必要があります。 コンポーネントには、タイトルの日付と金額を保存する独自の状態があります。

 # app/assets/javascripts/components/record_form.js.coffee @RecordForm = React.createClass getInitialState: -> title: '' date: '' amount: '' render: -> React.DOM.form className: 'form-inline' React.DOM.div className: 'form-group' React.DOM.input type: 'text' className: 'form-control' placeholder: 'Date' name: 'date' value: @state.date onChange: @handleChange React.DOM.div className: 'form-group' React.DOM.input type: 'text' className: 'form-control' placeholder: 'Title' name: 'title' value: @state.title onChange: @handleChange React.DOM.div className: 'form-group' React.DOM.input type: 'number' className: 'form-control' placeholder: 'Amount' name: 'amount' value: @state.amount onChange: @handleChange React.DOM.button type: 'submit' className: 'btn btn-primary' disabled: !@valid() 'Create record'
      
      





創造的なものではなく、インラインブートストラップフォームです。 入力値を設定して属性の値を決定する方法に注意してくださいonChange



属性は、キーが押されるたびに呼び出されるメソッドをアタッチして処理します。handleChange handleChange



メソッドは属性名を使用し、入力によりイベントがトリガーされ、値の状態が更新されます。

# app/assets/javascripts/components/record_form.js.coffee



@RecordForm = React.createClass

...

handleChange: (e) ->

name = e.target.name

@setState "#{ name }": e.target.value







名前がtitleと一致する場合、文字列インタープリターを使用して、 @setState title: e.target.value



に相当するキー@setState title: e.target.value



を動的に決定します。 しかし、なぜ@setState



を使用する@setState



でしょうか? 通常のJSオブジェクトで通常行うように、目的の<co値をstateに設定できないのはなぜですか? @setState



は2つのアクションを実行する必要があるため、これは次のとおりです。

  1. ステータスコンポーネントの更新
  2. 新しい状態に基づいてUIチェック/更新を計画する


コンポーネント内で状態を使用するたびに、この情報をメモリに保持することが非常に重要です。 renderメソッドの最後にある送信ボタンを見てみましょう

 # app/assets/javascripts/components/record_form.js.coffee @RecordForm = React.createClass ... render: -> ... React.DOM.form ... React.DOM.button type: 'submit' className: 'btn btn-primary' disabled: !@valid() 'Create record'
      
      





!@valid()



と共にdisabled



属性を定義しました。これは、ユーザーが提供したデータが正しいかどうかを評価するための有効なメソッドを実装することを意味します。

 # app/assets/javascripts/components/record_form.js.coffee @RecordForm = React.createClass ... valid: -> @state.title && @state.date && @state.amount
      
      





簡単にするために、空の行で@state



属性を再度検証するだけです。 したがって、状態が更新を受信するたびに、[ Create record



オン/オフのCreate record



]ボタンはデータの検証に依存します。



画像



これで、コントローラーとフォームが配置されました。 新しいレコードをサーバーに送信します。 イベントプレゼンテーションフォームを処理する必要があります。 タスクを完了するには、フォームの属性をonSubmit



と新しいhandleSubmit



メソッドに追加する必要があります( onChange



イベントでも同じことを行いonChange



た)。

 # app/assets/javascripts/components/record_form.js.coffee @RecordForm = React.createClass ... handleSubmit: (e) -> e.preventDefault() $.post '', { record: @state }, (data) => @props.handleNewRecord data @setState @getInitialState() , 'JSON' render: -> React.DOM.form className: 'form-inline' onSubmit: @handleSubmit ...
      
      





行ごとに新しいメソッドを確認しましょう。

  1. フォームを送信しない
  2. 現在のURLの新しいレコード情報をPOST
  3. 成功したコールバック




成功したコールバックは、このプロセスの鍵です。新しいレコードの作成が成功した後、誰かがこのアクションを報告する必要があり、ステータスが新しい値に更新されます。 このコンポーネントが設定(または@props



)を介して他のコンポーネントとやり取りすることについて言及したときのことを覚えていますか? だから、そうです。 現在のコンポーネントは、 @props.handleNewRecord



を介して親コンポーネントに情報を送り返し、新しいレコードが作成されたことを知らせます。



RecordForm



RecordForm



要素を作成するときは、 React.createElement RecordForm



handleNewRecord: @addRecord



などのメソッドへの参照をhandleNewRecord



設定を渡す必要があります。 さて、親レコードコンポーネントは「すべての場所」にあり、既存のすべてのレコードの状態があるため、新しく作成したレコードでこの状態を更新する必要があります

addRecord



内に新しいaddRecord



メソッドを追加し、h2タイトルの直後(renderメソッド内)に新しいRecordForm



要素を作成します。

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass ... addRecord: (record) -> records = @state.records.slice() records.push record @setState records: records render: -> React.DOM.div className: 'records' React.DOM.h2 className: 'title' 'Records' React.createElement RecordForm, handleNewRecord: @addRecord React.DOM.hr null
      
      





ブラウザを更新し、フォームに新しいレコードを入力し、[レコードのCreate record



]をクリックしても驚くことでCreate record



ません。今回は、すぐにレコードが追加され、クリック後にフォームが空になりました。 更新が完了しました。もちろん、バックエンドは新しいデータでいっぱいになりました。

画像



Reactと一緒に別のJSフレームワーク(たとえばhangar )を使用して同様の機能を作成する場合、POSTリクエストにRailsが必要とするCSRFトークンが含まれていないため、問題が発生する可能性があります。 jquery



を使用してバックエンドとやり取りするため、 Rails jquery_ujs



控えめなドライバーは、各AJAXリクエストにCSRFトークンを含めます。 かっこいい。



このセクションでコードの結果を確認するか、 ここで変更を確認できます。



コンポーネントの再利用





一部の(良い)メトリックがなければ、アプリは何を望みますか? ウィンドウの上部にいくつかの情報を使用したボックスを追加しましょう。 ボックス3では、クレジットの合計数、デビットの合計数、残高の値を示します。

3つのコンポーネントで作業しているように見えますか、それとも設定の1つだけですか?



テキストの量とタイプの設定を受け取る新しいAmountBox



コンポーネントを作成できます。 新しいファイルを作成すると、 javascripts/components/



からamount_box.js.coffee



が呼び出され、次のコードが挿入されます。

 # app/assets/javascripts/components/amount_box.js.coffee @AmountBox = React.createClass render: -> React.DOM.div className: 'col-md-4' React.DOM.div className: "panel panel-#{ @props.type }" React.DOM.div className: 'panel-heading' @props.text React.DOM.div className: 'panel-body' amountFormat(@props.amount)
      
      





ブートストラップパネルを使用するだけで、要素は「ブロック」メソッドで情報を表示し、設定の種類によって色を設定します。

また、設定の数を読み取り、通貨形式で表示するamountFormat



と呼ばれる、非常に簡単な形式のメソッドが含まれています。



注文には完全なソリューションがあります。 表示するデータに応じて必要な設定を渡すには、メイン要素内でこの要素を3回作成する必要があります。 メソッドの計算機を作成しましょう。 最初に、 Record



コンポーネントを開き、次のメソッドを追加します。

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass ... credits: -> credits = @state.records.filter (val) -> val.amount >= 0 credits.reduce ((prev, curr) -> prev + parseFloat(curr.amount) ), 0 debits: -> debits = @state.records.filter (val) -> val.amount < 0 debits.reduce ((prev, curr) -> prev + parseFloat(curr.amount) ), 0 balance: -> @debits() + @credits() ...
      
      





値が0より大きいすべてのレコードの貸方の合計。金額が0より小さいすべてのレコードの借方と残高値の合計。 これで、適切な場所に計算可能なメソッドができました。 内部にAmountBox



要素を作成し、メソッド( RecordForm



コンポーネントのすぐ上)をレンダリングするだけです。

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass ... render: -> React.DOM.div className: 'records' React.DOM.h2 className: 'title' 'Records' React.DOM.div className: 'row' React.createElement AmountBox, type: 'success', amount: @credits(), text: 'Credit' React.createElement AmountBox, type: 'danger', amount: @debits(), text: 'Debit' React.createElement AmountBox, type: 'info', amount: @balance(), text: 'Balance' React.createElement RecordForm, handleNewRecord: @addRecord ...
      
      





この機能はこれで完了です! ブラウザを更新します。 前に計算した3つのボックスが表示されているはずです。 しかし、待ってください! もっとあります! 新しいレコードを作成して、作品の魔法をご覧ください。



画像



コードの結果を見ることができます。

ここで修正



setState / replaceState:レコードを削除します





リストの次の機能は、レコードの削除です。 レコードテーブルに新しいアクション列が必要です。 この列には、エントリごとに削除ボタンがあり、UIの標準です。 前の例のように、Railsコントローラでメソッドを作成および削除する必要があります。

 # app/controllers/records_controller.rb class RecordsController < ApplicationController ... def destroy @record = Record.find(params[:id]) @record.destroy head :no_content end ... end
      
      





これは、この機能に必要なすべてのサーバー側コードです。 次に、 Records React



コンポーネントを開き、アクションのテーブルヘッダーの右側に列を追加します。

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass ... render: -> ... # almost at the bottom of the render method React.DOM.table React.DOM.thead null, React.DOM.tr null, React.DOM.th null, 'Date' React.DOM.th null, 'Title' React.DOM.th null, 'Amount' React.DOM.th null, 'Actions' React.DOM.tbody null, for record in @state.records React.createElement Record, key: record.id, record: record
      
      





最後に、レコードコンポーネントを開き、[削除]リンクを含む列を追加します

 # app/assets/javascripts/components/record.js.coffee @Record = React.createClass render: -> React.DOM.tr null, React.DOM.td null, @props.record.date React.DOM.td null, @props.record.title React.DOM.td null, amountFormat(@props.record.amount) React.DOM.td null, React.DOM.a className: 'btn btn-danger' 'Delete'
      
      





ファイルを保存し、ブラウザを更新すると、イベントが添付されていないボタンが機能しなくなります。



画像



いくつかの機能を追加しましょう。 リストを使用してRecordForm



コンポーネントから学んだように:

  1. Recordコンポーネントの子孫内のイベントを削除します(onClick)
  2. アクションを実行します(この場合、サーバーにDELETEリクエストを送信します)
  3. このアクションの親コンポーネントのレコードに通知する(設定によるハンドラーメソッドの送受信)
  4. レコードコンポーネントの状態を更新する




最初のステップを実装するために、同じ方法でOnClick



ハンドラーをRecord



に追加できOnClick



onSubmit



ハンドラーをonSubmit



RecordForm



して、新しいレコードを作成します。 幸いなことに、Reactはほとんどの一般的なブラウザーイベントを通常の形式で実装しています。 したがって、ブラウザ間の互換性について心配する必要はありません(イベントの完全なリストはこちらで確認できます)。



記録コンポーネントを再度開き、次のように「 handleDelete



な」削除ボタンに新しいhandleDelete



メソッドとOnClick



属性を追加します。

 # app/assets/javascripts/components/record.js.coffee @Record = React.createClass handleDelete: (e) -> e.preventDefault() # yeah... jQuery doesn't have a $.delete shortcut method $.ajax method: 'DELETE' url: "/records/#{ @props.record.id }" dataType: 'JSON' success: () => @props.handleDeleteRecord @props.record render: -> React.DOM.tr null, React.DOM.td null, @props.record.date React.DOM.td null, @props.record.title React.DOM.td null, amountFormat(@props.record.amount) React.DOM.td null, React.DOM.a className: 'btn btn-danger' onClick: @handleDelete 'Delete'
      
      





削除ボタンをクリックhandleDelete



はAJAXリクエストをサーバーに送信します

バックエンドでレコードを削除し、その後、 handleDeleteRecord



を介してこのアクションについて親コンポーネントに通知します。設定でハンドラーを使用できるため、親コンポーネントでのRecord要素の作成を規制する必要があります。

追加のhandleDeleteRecord



プロパティを有効にし、祖先に実際のハンドラメソッドを実装するには:

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass ... deleteRecord: (record) -> records = @state.records.slice() index = records.indexOf record records.splice index, 1 @replaceState records: records render: -> ... # almost at the bottom of the render method React.DOM.table React.DOM.thead null, React.DOM.tr null, React.DOM.th null, 'Date' React.DOM.th null, 'Title' React.DOM.th null, 'Amount' React.DOM.th null, 'Actions' React.DOM.tbody null, for record in @state.records React.createElement Record, key: record.id, record: record, handleDeleteRecord: @deleteRecord
      
      





基本的に、 deleteRecord



メソッドは、レコードの状態の現在のコンポーネントをコピーし、削除する必要があるレコードインデックスで検索を実行します。 かわいい標準のjs操作。



状態とやり取りする新しい方法を導入しましたが、replaceState



主な違いはsetState



replaceState



1つ目はオブジェクトの1つの状態キーのみを更新し、2つ目は送信する新しいオブジェクトでコンポーネントの現在の状態を完全に再定義することです。



コードの最後のビットを更新した後、ブラウザウィンドウを更新してエントリを削除しようとすると、次の2つのことが起こります。

  1. レコードがテーブルから消えます
  2. インディケーターはすぐに番号を更新する必要があります(これに他のコードは必要ありません)。




画像



ほぼ完了しましたが、最後の機能をインストールする前に、少しのリファクタリングを同時に適用して、新しいReact関数を導入できます。



リファクタリング:状態ヘルパー





最後の機能。テーブルのEdit



Delete



ボタンの後に追加のボタンを追加します。ボタンをクリックするEdit



と、ユーザーがレコードの内容を更新できるインラインフォームを開いて、行全体と読み取り専用状態を編集状態に切り替えます。更新されたコンテンツを送信するか、行に対するアクションをキャンセルすると、レコードは元の読み取り専用状態に戻ります。



前の章から推測したように、コンポーネント内のレコードの各状態を切り替えるために、いくつかのデータを処理する必要がありますRecord



。これは、Reactがリアクティブデータストリームと呼ぶものを使用する場合です。

編集フラグとhandleToggleメソッドをrecord.js.coffeeに追加しましょう:



 # app/assets/javascripts/components/record.js.coffee @Record = React.createClass getInitialState: -> edit: false handleToggle: (e) -> e.preventDefault() @setState edit: !@state.edit ...
      
      







編集フラグはデフォルトでオフになりhandleToggle



、編集をfalseからtrueに変更します。その逆も同様です。handleToggle



ユーザーでOnClick



イベントをトリガーするだけです



次に、read / read_and_edit行の2つのバージョンを制御し、編集に応じて条件付きで表示する必要があります。幸いなことに、視覚化メソッドがReact要素を返す限り、任意のアクションを自由に実行できます。我々は

いくつかのヘルパーメソッドを定義することができますrecordRow



し、recordForm



そして内容に応じて、可視化に条件付きでそれらを呼び出します@ state.edit







すでに最初のオプションがありrecordRow



、これが現在のレンダリング方法です。レンダリングコンテンツを新しいメソッドに移動しましょうrecordRow



追加のコードを追加します:

 # app/assets/javascripts/components/record.js.coffee @Record = React.createClass ... recordRow: -> React.DOM.tr null, React.DOM.td null, @props.record.date React.DOM.td null, @props.record.title React.DOM.td null, amountFormat(@props.record.amount) React.DOM.td null, React.DOM.a className: 'btn btn-default' onClick: @handleToggle 'Edit' React.DOM.a className: 'btn btn-danger' onClick: @handleDelete 'Delete' ...
      
      





余分に追加しましたReact.DOM



要素はからのシグナルを待っonClick



ていますhandleToggle







先に進みます。実装recordForm



は次の構造にする必要がありますが、各セルに入力フィールドがあります。ref



入力に新しい属性を使用し、利用可能にします。このコンポーネントは状態を処理しないため、この新しい属性により、コンポーネントはユーザーから提供されたデータを読み取ることができます。@refs:







 # app/assets/javascripts/components/record.js.coffee @Record = React.createClass ... recordForm: -> React.DOM.tr null, React.DOM.td null, React.DOM.input className: 'form-control' type: 'text' defaultValue: @props.record.date ref: 'date' React.DOM.td null, React.DOM.input className: 'form-control' type: 'text' defaultValue: @props.record.title ref: 'title' React.DOM.td null, React.DOM.input className: 'form-control' type: 'number' defaultValue: @props.record.amount ref: 'amount' React.DOM.td null, React.DOM.a className: 'btn btn-default' onClick: @handleEdit 'Update' React.DOM.a className: 'btn btn-danger' onClick: @handleToggle 'Cancel' ...
      
      





心配しないで。このメソッドはもっと大きくてもかまいませんが、単なるHTML構文です。

発言。@handleEdit



ユーザーがボタンをクリックしたときに呼び出しUpdate



レコードを削除する1つの実装として同様のスレッドを使用します。



それらの作成方法の違いに気づきましたReact.DOM.inputs



か?defaultValue



初期入力を設定する代わりにデフォルトを使用します。これは、値なしでのみ使用OnChange





すると最終的に読み取り専用入力として作成されるためです。



最後に、視覚化メソッドは次のコードに要約されます。



 # app/assets/javascripts/components/record.js.coffee @Record = React.createClass ... render: -> if @state.edit @recordForm() else @recordRow()
      
      





, , , .



画像



, Rails:

 # app/controllers/records_controller.rb class RecordsController < ApplicationController ... def update @record = Record.find(params[:id]) if @record.update(record_params) render json: @record else render json: @record.errors, status: :unprocessable_entity end end ... end
      
      





, handleEdit



, AJAX , , handleEditRecord



, @props



, , :

 # app/assets/javascripts/components/record.js.coffee @Record = React.createClass ... handleEdit: (e) -> e.preventDefault() data = title: React.findDOMNode(@refs.title).value date: React.findDOMNode(@refs.date).value amount: React.findDOMNode(@refs.amount).value # jQuery doesn't have a $.put shortcut method either $.ajax method: 'PUT' url: "/records/#{ @props.record.id }" dataType: 'JSON' data: record: data success: (data) => @setState edit: false
      
      





簡単にするために、ユーザーデータをチェックせず、React.findDOMNode



@ refs.fieldName



).valueを介してそれらを読み取り、バックエンドにそのまま送信します。成功のために状態を更新して編集モードに切り替えることはオプションですが、ユーザーは確かにそのことに感謝します。



最後に重要なことですが、レコードコンポーネントの状態を更新して、前のレコードを新しいバージョンのレコードの子孫で上書きし、Reactに魔法をかける必要があります。実装は次のようになります。

 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass ... updateRecord: (record, data) -> index = @state.records.indexOf record records = React.addons.update(@state.records, { $splice: [[index, 1, data]] }) @replaceState records: records ... render: -> ... # almost at the bottom of the render method React.DOM.table React.DOM.thead null, React.DOM.tr null, React.DOM.th null, 'Date' React.DOM.th null, 'Title' React.DOM.th null, 'Amount' React.DOM.th null, 'Actions' React.DOM.tbody null, for record in @state.records React.createElement Record, key: record.id, record: record, handleDeleteRecord: @deleteRecord, handleEditRecord: @updateRecord
      
      





前のセクションで学んだReact.addons.update



ように、状態の変更を使用すると、より具体的な方法につながる可能性があります。レコードとレコード間の最後のリンクは@updateRecord



handleEditRecord



設定を介して渡されるメソッドです。



ブラウザを最後に更新して、既存のエントリを更新してみてください。ページ上部のボックスが、変更したすべてのエントリを追跡する方法に注意してください。

画像



できました!

小さなRails + Reactアプリケーションをゼロから作成しました!



コードの結果はこちらで確認できますが、新しい変更はこちらです。



最終的な考え:React.js、シンプルさと柔軟性





Reactの機能のいくつかを見て、新しい概念を導入することはほとんどないことを学びました。 XまたはYフレームを言う人々のコメントを聞きました。JavaScriptは、新しく導入されたすべての概念のために急な学習曲線を持っていますが、これはReactのケースではありません。イベントハンドラーやバインディングなどの基本的なJavaScriptの概念を実装し、学習と理解を容易にします。繰り返しになりますが、その長所の1つはそのシンプルさです。



また、例を通して、「アクティブな作業」と「CoffeeScript、JQuery、Turbolinks、およびその他のレールとの連携」レールオーケストラに統合する方法を学びました。しかし、これが望ましい結果を達成する唯一の方法ではありません。たとえば、Turbolinksを使用しない場合(react_ujsは不要)、react-reils gemの代わりにRailsアセットを使用できます。JBuilderを使用して、JSONオブジェクトをレンダリングする代わりに、より複雑なJSON応答を作成できます。ただし、同じ素晴らしい結果を得ることができます。



All Articles