📜 ⬆️ ⬇️

React.js - Guide for Rails developers

At the initial level, such transfers are my contribution to the development of the rails community.
Further in the text everything that is in italics, my comments (there will not be many of them)

image

Introduction to React.js
')
React.js is a new popular guy from the JavaScript team of frameworks, he stands out for his simplicity. When other frameworks implement the full MVC (Model View Controller) approach, we can tell React to implement only the View (Mapping) (fact - some people rewrite part of the mapping (V) of these frameworks using React).

Reactor applications are based on the two basic principles of Components and States. Components may consist of smaller components built-in or custom. The states that the guys from Facebook call a one-way reactive data stream, meaning that our interface (UI) will respond to every state change.

One good feature of React.js is that it does not require any additional dependencies, which ensures its connectivity with any js library. Using this, we will include it in our Rails stack to create an external interface or you can say to create "Rails on steroids."

Layout for tracking application expenses



For this guide, we will create a small application from scratch to track our actions. Each record ( further, the same as a Record ) will consist of a date, name and amount. A record will be treated as a Credit if its amount is greater than zero, otherwise it will be considered a cash card. Here is the project layout:

image

In total, the application will behave like this:
  1. When the user creates a new record through the horizontal form, it will be inserted into the table of records.
  2. User can edit any existing entry.
  3. Clicking the Delete button will remove the association from the table.
  4. Adding, editing or deleting an existing entry will update the amount in boxes at the top of the page.


Initializing React.js to a Rails project



First of all, we need to create our new project, let's call it Accounts.
rails new accounts 

For the user interface of this project we will use bootstrap . The installation process is beyond the scope of this article, but you can install the official bootstrap-sass using instructions from the git repository .

Now our project is installed. Continue to connect React . For this guide, I decided to connect it from the official gem because we will use some cool features of this gem, but there is another way to achieve our goal of using Rails or we can download the source code from the official page and paste into our javascripts folder.

If you have been developing Rails applications, you know how easy it is to install react-rails : Add react-rails to your Gemfile
  gem 'react-rails', '~> 1.0' 

Then kindly tell the rails to set a new gem.
 bundle install 

React-rails comes with installing a script that will create the components.js file inside the app/assets/javascripts folder where our React components will live.
 rails g react:install 

If you look in your application.js file after launching the installation, you will see 3 new lines:
 //= require react //= require react_ujs //= require components 

Essentially, this includes the React library, components, and similar files are stored in ujs As you might guess from the file name, react-rails contains an unobtrusive JS driver that will help you install our React components and also handle Turbolinks events.

Resource creation



We will create a Record resource, which will consist of the date (date) of the title (title) and the amount (amount).
Instead of using scaffold 'a generation, we will use a resource generator,
we will not use all the files and methods created using the scaffold generator. Otherwise, it would be possible to run the scapold and then delete unused files / methods, but our project would be a bit dirty in that case. After that, inside the project, run the following command:
 rails g resource Record title date:date amount:float 

After this magic, we have a new model (Model) controller (Controller) and routes (routes). Now we will create a database and start the migrations:
 rake db:create db:migrate 

Plus, you can create a couple of records through
 rails console Record.create title: 'Record 1', date: Date.today, amount: 500 Record.create title: 'Record 2', date: Date.today, amount: -100 

Do not forget to start your server
 rails s 

Done! We can write code.

Nested Components: Records List



For our first task, we need to render any record created within the table.
First, we need to create an index action inside the RecordsController controller:
 # app/controllers/records_controller.rb class RecordsController < ApplicationController def index @records = Record.all end end 

Now we need to create a new index.html.erb file in apps/views/records/ , this file will be the bridge between our Rails application and the React components. To accomplish this task, we will use the helper method react_component , which gets the name React, the component we want to render along with the data that we pass to it.
 <%# app/views/records/index.html.erb %> <%= react_component 'Records', { data: @records } %> 

It is worth noting that this helper is provided by the react-rails gem, if you decide to use another integration React method, this helper will not be available.

Now you can go to localhost:3000/records . Obviously something is wrong, all because there are no Records (React components). But if you take the generated HTML inside the browser, we can insert something like this.
 <div data-react-class="Records" data-react-props="{...}"> </div> 

With this markup, react_ujs will determine, we are trying to render the React component and create an instance of it, including the settings we send via react_component , in our case, the @records content

It's time to create our first component, inside the javascripts/components directory create a new file: records.js.coffee , this file will contain our Records component.
 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass render: -> React.DOM.div className: 'records' React.DOM.h2 className: 'title' 'Records' 

Each component requires a render method that will change the rendering of its components, the method must return an instance of the ReactComponent class, so when React implements the re-render it will be executed optimally.

Comment. In another case, an instance of ReactComponents inside the render method can be written using JSX syntax.
Equivalent to code above:
  render: -> `<div className="records"> <h2 className="title"> Records </h2> </div>` 

For me personally, when I work with CoffeeScript, I prefer to use React.DOM syntax because the code will be converted to a hierarchical structure, as in HAML On the other hand, if you try to integrate React into an existing project with ERB, you can reuse existing ERB code and convert it to JSX.

Refresh your browser

image

Fine. We have rendered our first React component. Now it's time to display our records.

In addition, the render method of the React components relies on the use of settings for exchanging with other components and states in order to understand whether re-rendering is necessary or not. We need to initialize the state and properties of our component with the required values.
 # app/assets/javascripts/components/records.js.coffee @Records = React.createClass getInitialState: -> records: @props.data getDefaultProps: -> records: [] render: -> ... 

The getDefaultProps method will initialize the settings of our components in the case when we forget to transfer data, when we instantiate it and the getInitialState method will generate the initial state of our components. Now we generally need to display records using our Rails view.

It seems that we need a helper method for formatting the number of lines, we can insert simple formatting of lines and make it available for all coffee files. Create a new utils.js.coffee file in javascripts/ with the following content:
 # app/assets/javascripts/utils.js.coffee @amountFormat = (amount) -> '$ ' + Number(amount).toLocaleString() 

We need to create a new Record component, display each individual record, create a new record.js.coffee file in the javascripts/components directory and insert the following code:
 # 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 component should be displayed in a table column containing a cell for each attribute of the record. Do not worry about these null in React.DOM.* Calls, this means that we are not passing attributes to the components React.DOM.* we will update the render method inside the Records components with the following code:
 # 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 

Did you see what just happened? We created a table with a header and body inside. We created a Record element for each existing record. In other words, we put in build-in / custom React components, Cool. true?

When we have dynamic inheritors (in our case, records), we need to provide a configuration key for dynamic generation of items, so React does not have a long update time for our UI (user interface) because we are passing
key: record.id along with the current record when creating a Record element. If we do not do this, we need to get a warning in our JS browser console (and probably in the near future, sometimes, get a headache).

image

You can see the code for this section here or you can see the section changes .

The relationship between parents and children: creating Records



Now we display all the created records. It would also be great to include a form for creating a new record. Let's add this feature to our React / Rails application. First, we need to add our controller's create method (don't forget to use _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 

Next, we need to create a React component and track the creation of a new record. The component will have its own state to store the date (date) title (title) and amount (amount).
 # 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' 

Nothing creative, just the inline form of bootstrap. Notice how we determine the attribute value by setting the input's value (input's), the onChange attribute attaches and processes the method that was called with each keystroke, the handleChange handleChange method will use the attribute name, which input caused the event and updated the value state.
# app/assets/javascripts/components/record_form.js.coffee

@RecordForm = React.createClass
...
handleChange: (e) ->
name = e.target.name
@setState "#{ name }": e.target.value

We simply use the string interpreter to dynamically define the key of keys equivalent to @setState title: e.target.value when the name matches title. But why should we use @setState ? Why can't we just set the desired <co value in state as we usually do in regular> JS objects? Because @setState must perform 2 actions, this is:
  1. Update Status Components
  2. Plan UI check / update based on new state

It is very important to have this information in memory every time we use the state inside our component. Let's look at the submit button, only at the end of the render method.
 # 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' 

We defined the disabled attribute along with the value !@valid() , implying that we are going to implement a valid method to evaluate if the data provided by the user is correct.
 # app/assets/javascripts/components/record_form.js.coffee @RecordForm = React.createClass ... valid: -> @state.title && @state.date && @state.amount 

For simplicity, we only validate the @state attribute with empty lines again. Thus, each time the state receives updates, the Create record button on / off depends on the validation of the data.

image

Now our controller and form are in place. The time has come to send our new record to the server. We need to process the event presentation forms. To accomplish the task, we need to add an attribute of our form to onSubmit and a new handleSubmit method (we did the same with the onChange event).
 # 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 ... 

View the new method line by line:
  1. Prevent submission form
  2. POST new record information of the current URL
  3. Successful callback


A successful callback is the key to this process, after successfully creating a new record, someone must report this action, and the status is updated to the new value. Do you remember when I mentioned that this component interacts with other components through settings (or @props )? So, it is. Our current component sends information back to the parent component via @props.handleNewRecord informing about the creation of a new record.

As you could guess when we create our RecordForm element, we need to pass a handleNewRecord setting with a link to a method in it, something like React.createElement RecordForm , handleNewRecord: @addRecord . Well, the parent Records component is “everywhere” as it has a state with all of the existing records. We need to update this state with the new record created.
Add a new addRecord method inside the records.js.coffee and create a new RecordForm element, just after the h2 title (inside the render method).
 # 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 

Update your browser, fill out the form with a new record, click on Create record not surprising, this time the record was added almost immediately and the form became empty after clicking. The update was just completed, of course the backend was filled with new data.
image

If you use another JS framework together with React ( Angular for example) and create similar features, then you may have problems, because your POST request does not include the CSRF token required by Rails, so why haven't we encountered this question? Just because we use jquery to interact with our backend and Rails jquery_ujs unobtrusive driver, will include a CSRF token on each of our AJAX requests. Cool.

You can see the result of the code in this section, or see the changes here.

Component reuse



What would the application want without some (good) indicators? Let's add some boxes at the top of our window with some information used. We denote 3 boxes for boxes: Total amount of credits, total amount of debit and balance.
Does this look like working with three components or maybe just one of the settings?

We can create a new AmountBox component that will receive the settings: the amount of text and type. Creating a new file will call amount_box.js.coffee from javascripts/components/ and insert the following code:
 # 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) 

We simply use the bootstrap panel, the element displays information in the "blocky" method and sets the color through the type of setting.
We also have an included and very simple number of formatted methods called amountFormat that read the number of settings and display it in a currency format.

There is a complete solution in the order. We need to create this element 3 times within our main component to transfer the required settings depending on the data we want to display. Let's create calculation methods. First, open the Record component and add the following method:
 # 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() ... 

The amount of credits all entries with a value greater than 0. The sum of debits of all entries with a sum less than 0 and the value of the balance. Now we have in the right place calculating methods. We just need to create the AmountBox element inside, to render the method (just above the RecordForm component)
 # 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 ... 

We are done with this feature! Update your browser. You must have seen 3 displayed boxes, we calculated the amounts earlier. But wait! Is there some more! Create a new record and see the magic in the work.

image

You can see the result code
corrections here

setState / replaceState: deleting entries



The next feature on our list is the deletion of a record. We need a new action column in our table of records. This column will have a Delete button for each entry, a pretty standard UI. As in our previous example, we need to create and delete a method in our Rails controller.
 # app/controllers/records_controller.rb class RecordsController < ApplicationController ... def destroy @record = Record.find(params[:id]) @record.destroy head :no_content end ... end 

This is all the server side code we need for this feature. Now open your Records React component and add a column in the actions to the header in the table header.
 # 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 

Finally, open the Record component and add an extra column with the Delete link.
 # 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' 

Save your file, update your browser and We have a non-working button without any events attached to it.

image

Let's add some functionality. As we learned from our RecordForm component using the list:
  1. Delete the event of the internal child component Record (onClick)
  2. Perform action (send DELETE request to the server in this case)
  3. Notify Records of parent components about this action (sending / receiving handler method through settings)
  4. Update Record Component State


To implement the first step, we can add an OnClick handler to the Record in the same way, we added a handler for onSubmit to the RecordForm to create new records. Fortunately for us, React implements most of the common browser events in a normal way. Therefore, we do not have to worry about cross-browser compatibility (you can look at the full list of events here ).

Open the recording component again, add a new method, the handleDelete and the OnClick attribute to our “useless” delete button as follows:
 # 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' 

When the click on the delete button occurs, the handleDelete sends an AJAX request to the server.
to delete an entry on the backend and after that the parent component will be told about this action through the handleDeleteRecord handler is available through the settings, this means we need to regulate the creation of Record elements in the parent component,
to enable the additional property handleDeleteRecord , as well as implement the actual handler method in the ancestors:
 # 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 

Basically, our deleteRecord method copies the current record state component, the search index of the record you want to delete. cute standard js operations.

We have introduced a new way to interact with the state of replaceStatethe main difference between setStateand replaceStateis that the former will only update a state of an object key, and the second will be completely override the current state of the component to any new object that we send.

After updating the last bit of the code, refresh the browser window and try to delete the record, two things should happen:
  1. The record should disappear from the table
  2. The indicator should instantly update the count (no other code is needed for this).


image

We are almost done, but before installing the last feature, we can apply a small refactoring at the same time, introduce a new React function.

Refactor: State Helpers



Last feature. We will add an additional button Editafter each Deletebutton to our table. When we click on the button, Editit will switch the entire line and readonly state to the editing state by opening the inline form, where the user can update the contents of the records. After submitting the updated content or canceling the action to the line, the record will return to its original read-only state.

As you guessed from the previous chapter, we need to process several data to switch each state of records within our component Record. This is a case of using what React calls reactive data streams.
Let's add the edit flag and the handleToggle method to record.js.coffee:

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


The default edit flag will be turned off, and the handleToggleedit will change from false to true, and vice versa, we just need to start handleTogglean OnClickevent with the user .

Now we need to manage two versions of the read / read_and edit line and display them conditionally depending on the editing. Fortunately for us, as long as our visualization method returns the React element, we are free to perform any actions in it; we
we can define several auxiliary methods recordRowand recordFormcall them conditionally inside the visualization depending on the content @ state.edit.

We already have the first option recordRow, this is our current visualization method. Let's move the rendering content to our brand new method recordRowand add additional code to it:
 # 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' ... 

We just added an extra React.DOM. A member waits for a signal from onClickfor a call. handleToggle

Move on. The implementation recordFormshould be of the following structure but with an input field in each cell. We will use the new refattribute for our inputs, make them available; since this component does not process the state, this new attribute will allow our component to read the data provided by the user through@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' ... 

Do not worry. This method could be more but it is just an html syntax.
Comment.We call @handleEditwhen the user clicks a button Update, we are going to use the same thread as one implementation to delete the records.

Did you notice the difference in how they are created React.DOM.inputs? We use defaultValuethe default instead of specifying the initial input data, this is because using only the value without OnChange
will eventually be created only for reading input.

Finally, the visualization method comes down to the following code:

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

You can update your browser to play with the new switching behavior, but it does not represent any changes, since we did not realize the real possibility of updating.

image

To handle updates of records, we need to add an update method to our Rails controller:
 # 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 

Let's go back to our record component, we need to implement a method handleEditthat will send an AJAX request to the server with updated record information, then it notifies the parent component by sending an updated version of the record using the method handleEditRecord, this method will be received through @props, just as we did it before when deleting records:
 # 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 

For simplicity, we did not check the user data, we simply read it through React.findDOMNode( @ refs.fieldName) .value and send it literally to the backend. Updating the status to switch to edit mode for success is not mandatory, but the user will certainly thank us for it.

Last but not least, we just need to update the state on the Records component to overwrite the old record with the new version of the child record and let React perform its magic. The implementation might look like this:
 # 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 

As we learned in the previous section, using a React.addons.updatechange in our state can lead to more specific methods. The final link between Records and Record is the method @updateRecordit is transmitted through the handleEditRecordsettings.

Update your browser for the last time and try to update some existing entries, notice how the boxes at the top of the page, monitor each entry that you change.
image

We have done!
We just built a small Rails + React application from scratch!

You can see the result of the code here new edits here

Final thought: React.js, simplicity and flexibility



We looked at some of the functionality of React and found out that it barely introduces new concepts. I heard comments like people say X or Y framework, JavaScript has a steep learning curve due to all the newly-introduced concepts, this is not a React case; it implements basic JavaScript concepts such as event handlers and bindings, which makes it easy to learn and learn. Again, one of his strengths is his simplicity.

We also learned by example how to integrate it into “active work and how well it plays along with CoffeeScript, jQuery, Turbolinks, and the rest of the rails.” The rail orchestra, so to speak. But this is not the only way to achieve the desired results. For example, if you don’t use Turbolinks (which means you don’t need react_ujs) you can use Rails assets instead of the react-reils heme, you could use JBuilder to build more complex JSON responses instead of rendering JSON objects; however, you can still get the same excellent results.

Source: https://habr.com/ru/post/281735/


All Articles