5.4 Домашняя страница


Настала пора создать нашу первую страницу, используя готовый макет. Мы начнём с домашней страницы, так как она является главной в приложении и осуществляет навигацию пользователя по его ключевым разделам.



5.4.1 Стиль кода и методология БЭМ


Перед тем как мы приступим к реализации нашей первой страницы приложения, важно установить определенные соглашения по наименованию CSS-классов, компонентов, элементов со всей командой. Все члены команды должны придерживаться единого установленного стиля кода. Если этим пренебречь, в коде проекта очень скоро начнётся настоящая вакханалия! На чтение такого кода будет уходить очень много времени и каждый компонент будет резать глаза. Будет создаваться впечатление, что код писали индусы из 10 разных сёл. Эту плачевную ситуацию лучше всего предусмотреть заранее и предпринять все меры по её недопущению!

Не менее важно установить единый стиль написания компонентов. Они не должны быть разношёрстными, все методы должны идти в определённом порядке. Есть очень хорошие ресурсы по стилю написания кода JavaScript, а также стилю кода React/JSX. Я настоятельно рекомендую вам усвоить информацию из этих источников! Поверьте - это очень важно. Ваш код будет красив, ваши компоненты будут простые, а читать исходники будет интереснее, чем смотреть “игры престолов”.

Со стилем кода мы разобрались, теперь стоит рассказать ещё об одной вещи. Когда мы пишем разметку наших компонентов, то часто используем CSS-классы. Стиль их имён тоже имеет большое значение. Во-первых - единый стиль имён легко читать. А во вторых, правильно подобранный стиль имён позволит избежать уменьшения производительности UI.

Если кто-то ещё не знаком, то самое время познакомиться с замечательной методологией, под названием БЭМ. Я опять же настаиваю на том, чтобы вы полностью изучили все разделы сайта. Это ощутимо поднимет ваш уровень знаний и откроет глаза на многие, казалось бы, незаметные вещи. Материал читается быстро и легко, а пользу трудно переоценить.

Мы в своём коде будем использовать вот это соглашение. То есть схема именования наших CSS-классов будет такой:


Код
    
  BlockName-ElemName_modName_modVal
  

  • Имена записываются латиницей.

  • Имена блоков и элементов пишутся с заглавной буквы. Имена модификаторов — со строчной.

  • Каждое слово внутри имени пишется с заглавной буквы.

  • Имя элемента отделяется от имени блока одним дефисом (-).

  • Разделители имени и значения модификаторов совпадают с классической схемой.





5.4.2 Используем SASS


Если вы ещё не знакомы с этим мощным расширением CSS, самое время его изучить. Есть отличная русскоязычная документация. Очень скоро мы создадим некоторые полезные CSS классы, используя синтаксические конструкции SASS. Рассказывать о нём здесь не имеет смысла, куда красноречивее расскажут сами создатели. А мы, двигаемся дальше...



5.4.3 Используем flexbox


Эта полезная возможность CSS обеспечит нам большую скорость разработки стилей. Многие, не исключая меня, любят её за простоту и потрясающие возможности по компоновке элементов разметки.

Вы, возможно, уже использовали flex-ы и можете заявить, а как же кроссбраузерность? Не “поедет” ли разметка в некоторых браузерах? В этом разделе документации проекта create-react-app написано, что проект умеет делать пост обработку CSS и самостоятельно добавлять вендорские префиксы с помощью Autoprefixer. Нужно лишь указать целевые браузеры в browserslist в файле package.json. Отличный русскоязычный источник по flexbox вы можете найти здесь.



5.4.4 Bootstrap и другие известные UI библиотеки


Все веб-разработчики слышали или использовали такую популярную библиотеку как Bootstrap. Однако сообщество React создало для себя и другие богатые библиотеки: React Bootstrap, Material UI, React Virtualized, Blueprint, Semantic UI React, React Toolbox и многие другие. Расширенный список вы можете посмотреть здесь. Эти библиотеки предлагают вам изобилие разных компонентов на любой вкус.

Когда вы начинаете свой проект, лучше выбрать для себя одну такую библиотеку, как основу. Если подключить несколько - есть вероятность конфликтов JS кода и стилей, а также появления странных багов. По возможности библиотека должна реализовывать все типовые компоненты: модальные окна, кнопки, таблицы, табовые панели и пр. - чтобы вы не писали их сами. Затем, недостающие компоненты можно искать и подключать по отдельности, либо писать самостоятельно.

В нашем приложении мы будем использовать Bootstrap и React-Bootstrap. Из первой мы где-то используем, а где-то переопределим кое-какие стили. А из второй будем использовать готовые React компоненты.

Давайте добавим модуль bootstrap в наше приложение:



О том как подключить стили Bootstrap отлично написано здесь. Мы последуем этим инструкциям и сперва создадим файл main.scss - это будет наш главный файл, где будем подключать различные стили, как библиотечные, так и свои собственные. Затем подключим его в файле index.scss.

Внимание! Как вы заметили, файлы имеют расширение .scss - это расширение файла SASS. В таком файле можно использовать все мощные возможности этого фреймворка. Главное не переусердствуйте :)



Мы могли бы подключать стили и прямо в файле index.scss, однако вариант с файлом main.scss более гибкий. Помимо него мы сможем в index.scss подключить и любой другой необходимый файл. Просто дополнительный промежуточный уровень для гибкости.

Далее согласно документации в файле main.scss подключим стили bootstrap:


Код
    
  @import '~bootstrap/scss/bootstrap.scss';
  





Внимание!

Важно! Если вы подключаете стили из node_modules, то в импорте должны использовать префикс ~.

Их можно переопределить. Для этого достаточно написать свои собственные стили сразу после этой строки подключения:


Код
    
  @import '~bootstrap/scss/bootstrap.scss';
  
  .my-custom-style {
    font-size: 16px
  }
  

Однако такой способ пригоден только в тех случаях, когда приложение маленькое. Для больших проектов с множеством переопределений при таком способе очень быстро начинает страдать скорость поиска нужного стиля и читабельность, особенно у тех, кто знаком с вашим проектом совсем недавно. Чтобы этого избежать, переопределяемые стили лучше группировать по отдельным файлам, которые затем следует подключить в main.scss после строки подключения стилей bootstrap (и других сторонних библиотек).

Для переопределения стилей в нашем приложении, давайте создадим папку custom, где разместим все наши файлы переопределения стилей bootstrap. В этой папке создадим два файла переопределения common.scss и buttons.scss. В первом будем хранить универсальные стили, а во втором будем переопределять стили кнопок bootstrap, согласно нашей цветовой гамме.




В файле common.scss мы определили циклы для генерации полезных CSS классов, используя синтаксис SASS. Представляете, сколько заняло бы места и времени написать их, используя простой CSS? При этом читабельность просто отличная!

В файле buttons.scss мы пока определили один класс .btn-primary. Он переопределяет оригинальный CSS-класс bootstrap. Мы добавим ещё переопределений по мере продвижения.

Теперь, когда со стилями мы разобрались, пора приступить к созданию компонента домашней страницы.

Ещё хочу обратить ваше внимание на файл index.js. В нём мы сначала подключаем стили, а потом компоненты. Это важно, так как если сделать наоборот, общие стили будут переопределять стили компонентов.



5.4.5 Компонент домашней страницы


Итак, давайте создадим компонент домашней страницы:




Теперь проанализируем код приложения.



Мы создали компонент <Home/>. В начале файла идут все необходимые импорты. Обратите внимание, я расположил их в определённом порядке, разделяя пустыми строками. Это важно, так как сильно улучшает читабельность. В общем случае порядок импортов может быть такой:

  1. Импорт самой библиотеки React: import React, { Component } from 'react'

  2. Пустая строка

  3. Импорт всех сторонних библиотек. Эти библиотеки также можно объединять в группы, отделяя последние пустой строкой:

    Код
        
      import cn from 'classname'
      
      import { connect } from 'react-redux'
      import { Link } from 'react-router-dom'
      import { bindActionCreators } from 'redux'
      
      import { Badge, Button } from 'reactstrap'
      
      

  4. Пустая строка

  5. Импорт всех необходимых пользовательских компонентов

  6. Пустая строка

  7. Импорт стилей компонента

  8. Пустая строка

  9. Импорт всех необходимых redux-действий

  10. Пустая строка

  11. Импорт всех необходимых пользовательских утилит

Представленный порядок импортов, может быть изменен или дополнен командой. Очень важно, чтобы все члены команды его придерживались, тогда это принесёт ощутимую пользу в плане читабельности. Вот более полный пример того, как можно расположить импорты:


Код
    
  import React, { Component } from 'react'
  
  import cn from 'classname'
  
  import { connect } from 'react-redux'
  import { Link } from 'react-router-dom'
  import { bindActionCreators } from 'redux'
  
  import { Badge, Button } from 'reactstrap'
  
  import Tabs from '../../../components/Tabs/Tabs'
  import Table from '../../../components/Table/Table'
  import Loader from '../../../components/Loader/Loader'
  import SearchField from '../../../components/SearchField/SearchField'
  import Breadcrumbs from '../../../components/Breadcrumbs/Breadcrumbs'
  
  import './Organizations.scss'
  
  import * as sideBarActions from '../../../redux/sidebar/sideBarActions'
  import * as organizationListActions from '../../../redux/organization/list/organizationListActions'
  import * as organizationCountActions from '../../../redux/organization/count/organizationCountActions'
  
  import { getSideBarItems } from '../SideBarItems'
  
  import { PAGINATION } from '../../../lib/Constants'
  
  import { path } from '../../../lib/ContextUtils'
  import { isEmpty, DateUtils } from '../../../lib/Utils'
  

Вы можете заметить ещё одну интересную деталь: импорты в пределах группы расположены в порядке длинны строки. Более короткая строка импорта идет перед более длинной. Так делать не обязательно, можно задавать и какой-то другой порядок (или вообще не задавать). Но по моему личному мнению, импорты в таком порядке смотрятся очень аккуратно, а значит и читабельность лучше.

С импортами мы разобрались. Далее мы объявляем все необходимые константы. В нашем случае это SECTIONS. В этой константе хранятся атрибуты для каждого раздела: название, ссылка и иконка. Реализация не обязана быть именно такой. Необязательно было сохранять в локальной константе иконки, хоть это и распространённая практика. Для раздела можно было бы создать компонент-функцию, например:


Код
    
  <Section title="События" href="/events" icon={Teeth} />
  

Далее в методе render() мы отрисовываем все разделы, используя метод map библиотеки underscore. Эта библиотека является очень мощным инструментом при работе с коллекциями, массивами и объектами. Она имеет богатый API и позволяет сильно сократить код, предоставляя готовые методы для многих типовых задач: поиск, сортировка, клонирование, определение типа, проверка на пусто и многое многое другое. Очень рекомендую использовать её в ваших проектах.



Отдельно стоит сказать об изображениях. В проекте мы используем файлы .svg. Они полезны тем, что позволяют изменять свой цвет и отлично масштабируются. В документации подробно описано как их можно подключать. А подключать их мы можем подключить двумя способами: как обычное изображение (например как файл .png) и как ReactComponent. Во втором случае изображение не будет загружаться как отдельный файл. ReactComponent - это специальное имя, которое говорит Create React App о том, что вы определяете компонент React, который будет отрисовывать SVG.

С учётом вышесказанного, подключение и использование .svg изображения в нашем проекте выглядит следующим образом:


Код
    
import { ReactComponent as House } from '../../images/house.svg'

<House className='Header-Icon'/>
  

Как видно, изображение представлено компонентом, которому мы можем назначить наш собственный CSS-класс. В этом классе мы задаём все необходимые стили изображения:


Код
    
  .Header-Icon {
    width: 45px;
    height: 45px;
    fill: white; // изменяем цвет
    margin-right: 20px;
  }
  

Цвет изображения можно изменить с помощью свойства fill.

Напоследок поговорим о компоненте <Header>. Он является общим для большинства страниц приложения, поэтому и реализован как отдельный переиспользуемый компонент. Он отображает название страницы, иконку, имя текущего пользователя и кнопку выхода из приложения. Стиль кнопки мы переопределили аж двумя классами: .btn-primary в buttons.scss и .Header-ExitBtn в Header.scss. В первом классе мы изменили лишь фон, а во втором переопределили границу. Дело в том, что такая граница кнопки характерна только для этой страницы. На другой странице такая граница может быть неприемлема.

Компонент <Header> использует библиотеку classnames. Это очень полезная библиотека, когда дело касается работы с CSS-классами. Она позволяет избежать конструкции типа:


Код
    
  className={'MyComponent ' + className + (isActive ? ' MyComponent_active' : '')}
  

Вместо этого можно записать:


Код
    
  className={cn('MyComponent', className, isActive && 'MyComponent_active')}
  

Более подробно можно почитать здесь.

Обратите внимание на CSS-классы: flex-1 d-flex flex-row justify-content-start align-items-center. Здесь мы не придумывали какие-то свои, а использовали уже готовые. Класс flex-1 взяли из нашего common.scss, а остальные нам предоставил bootstrap. Такой способ использования разных сущностей на одном DOM узле в БЭМ называется миксом. В случаях, когда свой собственный CSS класс будет избыточен мы вполне можем использовать этот подход.

Также компонент <Header> использует метод renderIcon. Он нужен для того, чтобы отрисовывать иконку текущей страницы.

Ну что ж, компонент домашней страницы готов, теперь можно перейти к реализации списка приёмов, использовав библиотеку для создания таблиц.