Rails та React: Справжнє використання (Частина 2)

12 хв. читання

У моєму попередньому пості (переклад) можете прочитати невеличке введення у React - JavaScript бібліотеку від Facebook для створення користувацьких інтерфейсів, а також про його відмінності від більш повноцінних MV- фреймворків. А зараз час використати декілька React компонентів у нашому Rails додатку.

Остаточний результат

Перш ніж ми продовжимо, хотів би з вами поділитись простеньким додатком, який я створив - результат того, що ми отримаємо, коли дійдемо до кінця цього посту. Він складається із:

  • Моделі Person.
  • Контролера HomeController із всього лиш одною дією index, яка є корінним шляхом (root route) додатку, а також місцем, де React компоненти перемальовуються в залежності від пошуку користувача, щоб відобразити результат.
  • Котролера PeopleController, котрий рендерить JSON результат пошуку.

Все це створено, використовуючи Rails 4.2.0beta та PostgreSQL, що дозволяє нам використовувати повнотекстовий пошук, про який я писав в одному з моїх останніх постів.

Ну і зображення як це буде виглядати: Rails and React app

Встановлюємо React

Найпростіший спосіб додати React в Rails проект - додати цих два ґема у ваш Gemfile:

gem 'react-rails', github: 'reactjs/react-rails'
gem 'sprockets-coffee-react'
  • react-rails - це офіційний ґем React для Ruby. Він додає необхідні assets до вашого проекту, які необхідні, щоб використовувати React, а також автоматично компілює ваш JSX код у звичайний JavaScript.
  • sprockets-coffee-react - це препроцесор, який надає можливість писати компоненти, використовуючи CJSX (CoffeeScript із JSX розміткою).

Щоб використовувати React у ненав'язливий спосіб, потрібно всього лише додати наступний код у ваш application.js.coffee:

... 
#= require react
#= require react_ujs

В такому разі усі компоненти, які були створені, використовуючи допоміжний ґем для представлення (view) (ми поговоримо про нього пізніше), будуть автоматично відмальовані, а також знищені, коли завантажиться інша сторінка.

А зараз ми вже готові повеселитись із React'ом.

Пишемо React компонент

Як я сказав перед тим, ми будемо використовувати JSX для написання наших компонентів... але що таке цей JSX? Це всього лиш перетворювач синтаксису JavaScript XML. Це означає, що ви можете писати на XML подібному синтаксису в своїх компонентах, і він буде перетворений у звичайні JavaScript функції. Цей синтаксис дуже допоможе під час написання методу render для кожного нашого компонента. Щоб дізнатись більше про JSX - почитайте Офійну документацію по Реакту.

Хорошою практикою створення компонентів є в першу чергу написання його на звичайному ERB або HAML (у моєму випадку), а тоді вже розділення кожного блоку, який матиме власну поведінку, у окремий компонент. В такому разі ми можемо використовувати цей HTML код для методу render після того, як він буде готовий. То ж давайте розпочнемо!

Головне представлення

Як я сказав перед тим, ми маємо HomeController, у якого є дія index та представлення для неї. Це все буде виглядати так:

# app/views/home/index.html.haml

%section
  %header
    %h1 Rails та React: Справжнє використання (Частина 2)
  = react_component 'PeopleSection', {}, :div

Ми використовуємо хелпер для представлень, наданий ґемом react-rails, який дозволяє нам відмалювати компонент PeopleSection, який не приймає жодних властивостей та обгортається у div. Просто як двері :)

Компонент PeopleSection

Всі свої компоненти я зберігаю в app/assets/javascripts/react, отже нам потрібно включити цю директорію в application.js.coffee:

# app/assets/javascripts/application.js.coffee

...
#= require react
#= require react_ujs
...
#=require_tree ./react

PeopleSection - наш батьківський компонент, він має поле пошуку та список людей, а також він відповідає за запит до нашого бек-енду потрібних даних, і передає дані списку людей, тобто компонентам-нащадкам.

# app/assets/javascripts/react/people/people_section.js.coffee

# @cjsx React.DOM

@PeopleSection = React.createClass
  # Відображає ім'я для дебагу
  displayName: 'PeopleSection'

  # Викликається перш ніж компонент використовують, а також встановлює початковий стан для методу render
  getInitialState: ->
    # Зміниться на true, коли компонент отримає якісь дані
    didFetchData: false
    # JSON масив people використовується для того, щоб відобразити картки із людьми в нашому представленні
    people: []

  # Викликається відразу після того, як компонент відмальований
  componentDidMount: ->
    # Витягнемо усіх людей...
    @_fetchPeople({})

  # AJAX запит до нашого PeopleController
  _fetchPeople: (data)->
    $.ajax
      url: Routes.people_path()
      dataType: 'json'
      data: data
    .done @_fetchDataDone
    .fail @_fetchDataFail

  # Якщо AJAX запит вдалий...
  _fetchDataDone: (data, textStatus, jqXHR) ->
    # Ми змінюємо стан компонента. Це змусить компонент та його нащадків
    # відмалюватись знову
    @setState
      didFetchData: true
      people: data

  # Якщо AJAX запит видав помилку...
  _fetchDataFail: (xhr, status, err) =>
    console.error @props.url, status, err.toString()

  # Перехоплює подію відправки форми компоненту PeopleSearch
  _handleOnSearchSubmit: (search) ->
    # Отримуємо дані з поля вводу
    @_fetchPeople
      search: search

  # І тепер те, як компоненти будуть вимальовуватись у залежності від
  # того, які задані властивості і стан...
  render: ->
    # Колекція PersonCard компонентів, які ми хочемо відобразити, 
    # використовуючи список людей, збережений у стані компонента
    cardsNode = @state.people.map (person) ->
      # PersonCard компонент із даними і властивостями, які містять всі 
      # JSON атрибути, які ми хочемо відобразити користувачу
      <personcard data="{person}/" key="{person.id}">

    # HTML який відображається, коли нікого не знайдено
    noDataNode =
      <div>
        <span classname="fa-stack">
          <i classname="fa fa-meh-o fa-stack-2x"></i>
        </span>
        <h4>No people found...</h4>
      </div>

    # Тут розпоничається те, що ми будемо відображати
    <div>
      # Це компонент PeopleSearch. Коли виконується метод 
      # onFormSubmit, компонент PeopleSection обробить його з допомогою
      # методу _handleOnSearchSubmit
      <peoplesearch onformsubmit="{@_handleOnSearchSubmit}/">
      <div>
        {
          # Якщо є що вимальовувати...
          if @state.people.length > 0
            {cardsNode}
          # І якщо є невірні дані і не знайдено людей 
          # вимальовується повідомлення про помилку
          else if @state.didFetchData
            {noDataNode}
        }
      </div>
    </peoplesearch></div>

Лінійка # @cjsx React.DOM на початку файлу потрібна для того, щоб Rails знав, що ми будемо використовувати JSX, і потрібно його компілювати у JavaScript. Як ви можете побачити, все це працює дуже просто:

  • Ми встановили початковий стан, тому компоненти вимальовуються користувачу.
  • Коли компонент вимальовується вперше, ми отримуємо людей, яких хочемо відобразити.
  • Якщо все гаразд, ми змінюємо його стан, що змушує компонент і його дітей перемальовуватись наново.
  • Запам'ятайте те, що React використовує віртуальний DOM для цього, тому він не перемальовує весь HTML знову, а приймає тільки ті зміни, які необхідні, тому це дійсно швидко.

Компонент PeopleSearch

Як ми могли побачити, цей компонент викликає подію, яка змушує компонент PeopleSection робити AJAX запит до нашого бек-енду. Давайте розглянемо як він працює:

# app/assets/javascripts/react/people/people_search.js.coffee

# @cjsx React.DOM

@PeopleSearch = React.createClass
  displayName: 'PeopleSearch'

  # Перехоплювач події відправки форми
  _handleOnSubmit: (e) ->
    e.preventDefault()

    # значення поля вводу пошуку
    searchValue = @refs.search.getDOMNode().value.trim()

    # Викликає свою власну подію onFormSubmit і приймає searchValue 
    @props.onFormSubmit(searchValue)

  render: ->
    <div>
      <div>
        <form onsubmit="{@_handleOnSubmit}">
          # посилання на атрибути використовується, щоб зсилатись 
          # на елементи в компоненті через @refs
          <input placeholder="Search people..." ref="search" type="search">
        </form>
      </div>
    </div>

Це по-справжньому простий компонент. Коли відправляється форма, викликається метод onFormSubmit і йому передається значення поля вводу пошуку. І це все.

Компонент PersonCard

Нашим останнім компонентом буде PersonCard. Він буде відповідати за відмальовування наших карток людей.

# app/assets/javascripts/react/people/person_card.js.coffee

# @cjsx React.DOM

@PersonCard = React.createClass
  displayName: 'PersonCard'
  render: ->
    # Використаємо додаток, щоб встановити основний клас для div
    cx = React.addons.classSet
    # Воно застосує кожен ключ, значення якого буде true 
    # до атрибуту className
    cardClasses = cx
      'card': true
      'female': @props.data.gender == 'female'
      'male': @props.data.gender == 'male'

    # А тут ми використовуємо ці класи
    <div>
      <header>
        <div>
           
          <img src="{@props.data.picture}">
        </div>
        <div>
          <h4>{@props.data.first_name} {@props.data.last_name}</h4>
          <ul classname="meta">
            <li>
              <i classname="fa fa-map-marker"></i> {@props.data.location}
            </li>
            <li>
              <i classname="fa fa-birthday-cake"></i> {@props.data.birth_date}
            </li>
          </ul>
        </div>
      </header>
      <div>
        <div>
          <p>{@props.data.headline}</p>
        </div>
        <ul classname="contact-info">
          <li><i classname="fa fa-phone"></i> {@props.data.phone_number}</li>
          <li><i classname="fa fa-envelope"></i> {@props.data.email}</li>
        </ul>
      </div>
    </div>

Це найпростіший компонент із трьох. Я використав props.data, передані компонентом PeopleSection, щоб відобразити їх в методі render. Так як ми використовували додатки реакту, щоб визначити атрибу className, не забудьте додати наступний код у файл application.rb, у іншому випадку він просто не буде працювати:

# config/application.rb
module RailsAndReact
  class Application < Rails::Application
    # ...
    # React config
    config.react.addons = true
    # ...
  end
end

Що далі?

Всього лиш за декілька хвилин я створив деякий простенький компонент на React, який дуже добре вписався у наш Rails додаток. Так як ви можете створювати по-справжньому крихітні компоненти, складність написання коду для них теж мізерна, тому навчитись використовувати React дуже просто.

В наступному пості ми створимо більш складні компоненти і подивимось на те, як вони спілкуються між собою. А тим часом можете дослідити сирцевий код, або поклацати демо.

Демо

Код

Помітили помилку? Повідомте автору, для цього достатньо виділити текст з помилкою та натиснути Ctrl+Enter
Codeguida 5.8K
Приєднався: 8 місяців тому
Коментарі (0)

    Ще немає коментарів

Щоб залишити коментар необхідно авторизуватися.

Вхід / Реєстрація