Сразу скажу, что сайт будет быстрее работать, если заменить Bootstrap на чистый CSS и JS. Эта статья про то, как быстро начать разрабатывать красивые web-приложения, а оптимизация это уже отдельный вопрос, выходящий за пределы этой статьи.

Для начала надо хотя бы немного разбираться в HTML, CSS, JavaScript, XML, DOM, ООП и уметь работать в терминале (командной строке).

Где брать материалы для изучения?
Для изучения HTML и CSS рекомендую htmlbook.ru
Для изучения JavaScript рекомендую learn.javascript.ru
Для изучения XML рекомендую msiter.ru/tutorials/uchebnik-xml-dlya-nachinayushchih
Про DOM можно почитать в уроке по JavaScript learn.javascript.ru/dom-nodes
Для изучения ООП рекомендую видеокурс proglib.io/p/oop-videocourse
Для изучения командной строки Windows рекомендую cmd.readthedocs.io/cmd.html
Для изучения терминала в Mac рекомендую ixrevo.me/mac-os-x-terminal
Если вы работаете в Linux, то bash и аналоги знаете, в крайнем случае man или help вам помогут.
Для изучения React использую learn-reactjs.ru (который является переводом официальной документации React: reactjs.org).
Для изучения Bootstrap использую bootstrap-4.ru (который является переводом официальной документации Bootstrap: getbootstrap.com).
Для того, чтобы подружить React и Bootstrap нашёл отличную статью webformyself.com/kak-ispolzovat-bootstrap-s-react

В этой статье сделаю выжимку минимально необходимого для работы и сделаем такой таймер:



Установка


Для начала нам нужен менеджер пакетов. Я выбрал npm, а он есть в Node.js
Так что первым делом устанавливаете Node.js на свою операционную систему с официального сайта: nodejs.org/en/download. С установкой вы сможете разобраться, так что процесс установки описывать не буду. Отмечу лишь, что под Ubuntu устанавливается просто:

sudo apt update
sudo apt install nodejs
sudo apt install npm

Через терминал проверяем, что Node.js и npm успешно установились:

nodejs -v
npm -v

Если при выводе возникнут ошибки – значит что-то прошло не так и надо разбираться, и возможно переустанавливать их. Если же выведется v c цифрами и точками – то всё хорошо.

Установим Create-react-app, чтобы потом можно было быстро создавать каркасы приложений:

npm install -g create-react-app

Далее создаём каркас приложения на React. Назовём наше приложение new-app. Если хотите создать приложение папке отличной от пользовательской папки – то сначала через терминал перейдите в неё с помощью команды cd. Итак, в терминале достаточно ввести 3 команды:

create-react-app new-app
cd new-app
npm start

Создаём приложение new-app. Переходим в папку new-app. Запускаем приложение. После этих строк должен запуститься браузер с приложением React по адресу http://localhost:3000



Терминал должен остаться открытым, без него не будет открываться страница приложения. Если вдруг закрыли – не беда. Достаточно с помощью команды cd перейти в папку приложения и запустить его командой npm start

Теперь установим Bootstrap

npm install bootstrap

Так же советуют установить к нему зависимости jquery и popper.js, но они нужны только для JS-части Bootstrap. Попробовал без них — CSS-часть Bootstrap нормально работает, так что следующие строки в терминале не обязательны:

npm install jquery popper.js

Далее надо внести изменения в файлы приложения, для этого переходим в папку new-app, где находится приложение в файл src/index.js добавляем строку, она должна быть первой:

import 'bootstrap/dist/css/bootstrap.min.css';

Если будете использовать jQuery, popper.js или JS-часть Bootstrap (модальные окна, анимации и т.д), то потребуется под первой строкой добавить ещё 3 строки:

import $ from 'jquery';
import Popper from 'popper.js';
import 'bootstrap/dist/js/bootstrap.bundle.min';

Теперь осталось запустить проект:

npm start

И снова откроется браузер по адресу http://localhost:3000 уже с приложением, облагороженным с помощью Bootstrap:


Так же для отладки React можно установить расширение «React Developer Tools» для браузера. Актуальные ссылки на расширение для Chrome и Firefox и другие варианты использования указаны в официальном репозитории github.com/facebook/react-devtools
Установка и первоначальная настройка на этом завершена.

JSX, компоненты и свойства


Давайте посмотрим, что нам сгенерировал create-react-app — исходные файлы лежат в каталоге src. Во-первых, посмотрим файл index.js – там несколько строчек импорта. По строчкам понятно, что они делают, так что не буду комментировать.

Самая важная строка в этом файле:

ReactDOM.render(<App />, document.getElementById('root'));

В ней рисуется страница приложения. В исходном HTML-файле находится элемент <div> с id=root. В этом <div> выводится компонент App, который рисуется функцией render класса ReactDOM. При этом компонент рисуется в форме, похожей на XML, которая и называется JSX (о котором позже).

Теперь перейдём в файл App.js, где находится реализация класса App, который наследуется от класса React.Component.

class App extends React.Component {
В классе вызывается метод <b>render()</b>, который рисует страницу на JSX:
<div className="App">
  <header className="App-header">
    <img src={logo} className="App-logo" alt="logo" />
    <p>
      Edit <code>src/App.js</code> and save to reload.
    </p>
    <a
      className="App-link"
      href="https://reactjs.org"
      target="_blank"
      rel="noopener noreferrer"
    >
      Learn React
    </a>
  </header>
</div>

JSX очень похож на HTML, при этом есть вставки JS-кода в фигурных скобках {}. И обязательно должен быть один корневой элемент, в данном случае <div>.

Чтобы лучше разобраться – сотрём весь код метода render(), и напишем простейший компонент:

class App extends React.Component {
  render() {
    return <h1>Привет, {this.props.name}!</h1>;
  }
}

А теперь вернёмся в файл index.js и исправим

ReactDOM.render(<App name="Мир" />, document.getElementById('root'));

После сохранения файлов – в браузере обновится страница. А теперь будем разбираться.

Концептуально, компоненты подобны JavaScript-функциям. Они принимают произвольные данные (называемые props) и возвращают React-элементы, описывающие что должно появиться на экране. Компоненты позволяют разделить UI на независимые, переиспользуемые части и работать с каждой из них отдельно.

Когда React видит, что элемент представляет собой пользовательский компонент, он передает все JSX-атрибуты в этот компонент единым объектом. Такой объект называется props.

В примере параметр name передаётся в компонент как атрибут тега <App> со значением «Мир». Далее в методе render() класса App в качестве результата функции внутри JSX, который фактически является HTML-шаблоном – в фигурных скобках {} указывается this – текущий класс, props – пользовательский объект, name – название параметра объекта.

Конструктор, жизненный цикл и изменение состояния


Помимо параметров, хранящихся в props можно хранить состояние объекта в state.

Сделаем таймер. Для таймера не нужны параметры, поэтому уберём параметры в index.js:

ReactDOM.render(<App/>, document.getElementById('root'));

А теперь в файле App.js заменим весь текст между import и export:

const INTERVAL = 100;

class App extends Component {
	
  constructor(props) {
    super(props);
    this.state = {value: 0};
  }

  increment(){
    this.setState({value: this.state.value + 1});
  }

  componentDidMount() {
    this.timerID = setInterval(() => this.increment(), 1000/INTERVAL);
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  render() {
    const value = this.state.value
    return (
      <div>
        <p>Таймер:</p>
        <p>
          <span>{Math.floor(value/INTERVAL/60/60)} : </span>
          <span>{Math.floor(value/INTERVAL/60) % 60} : </span>
          <span>{Math.floor(value/INTERVAL) % 60} . </span>
          <span>{value % INTERVAL}</span>
        </p>
      </div>
    );
  }
}

После вставки и сохранения этого кода на странице появится и автоматически запустится таймер.

Давайте разбирать этот код. До класса объявили константу, благодаря которой можно регулировать частоту обновления таймера.

Далее внутри класса идёт обязательный конструктор класса, в который передаётся props. Далее стандартная обработка конструктора родительского класса super(props) и определение состояния value через this – текущий объект. Это единственное место, где можно напрямую установить состояние. В остальных местах доступно только чтение, либо установка состояния специальным методом setState(), который используется в следующем методе increment() для увеличения состояния value на единицу.

В приложениях с множеством компонентов очень важно высвобождать ресурсы, занятые компонентами, когда они уничтожаются. Нам необходимо устанавливать таймер каждый раз, когда DOM отрисовывается в первый раз. В React это называется «монтированием/монтажом». Также нам нужно очищать этот таймер, каждый раз когда DOM, созданный компонентом, удаляется. В React это называется «демонтированием/демонтажём».

Для этого и используются методы componentDidMount() и componentWillUnmount(). В документации эти методы носят название «lifecycle hooks». Мы же будем для простоты называть их методами жизненного цикла. Метод componentDidMount() срабатывает после того, как компонент был отрисован в DOM. Это хорошее место, чтобы установить таймер. Очищать таймер будем в методе componentWillUnmount() жизненного цикла.

Обратите внимание, как мы в componentDidMount() сохраняем ID таймера прямо в this используя стрелочную функцию. В то время как this.props самостоятельно устанавливаются React-ом и this.state имеет определенное значение, вы свободно можете добавить дополнительные поля в класс вручную, если вам необходимо хранить что-нибудь, что не используется для визуального вывода. Если вы не используете что-то в render(), оно не должно находиться в состоянии state.

Далее на время выполнения render() в локальной константе value фиксируется значение состояния value. И далее с помощью математической функции floor(), которая округляет число в меньшую сторону, деления(/) и получение остатка от деления(%) получаем части таймера, которые далее выводим в одну строку после слова Таймер. Можно посмотреть результаты нашей работы.

Оформление с помощью Bootstrap


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

Начнём с оформления. Для этого в файл App.css добавим следующие строки:

.container-fluid {
  display: flex;
  flex-direction: column;
}

Благодаря встроенному в Bootstrap адаптивно-резиновому контейнеру container-fluid, который помогает создать полностью гибкий макет страницы или некоторого блока. Данный контейнер имеет 100% ширину. Сделаем контейнер flex, с направлением выстраивания элементов по вертикали – чтобы он занял всё пространство и его можно было выровнять по центру.

Теперь доработаем метод render() в App.js, чтобы применить стили Bootstrap и добавить пару кнопок. Для этого заменим возвращаемое методом значение на следующее:

<div class="container-fluid align-items-center">
  <h1 class="display-1">Таймер</h1>
  <h1 class="display-1">
    <span><kbd>{Math.floor(value/INTERVAL/60/60)}</kbd> : </span>
    <span><kbd>{Math.floor(value/INTERVAL/60) % 60}</kbd> : </span>
    <span><kbd>{Math.floor(value/INTERVAL) % 60}</kbd> . </span>
    <span><kbd>{value % INTERVAL < 10 ? '0' : ''}{value % INTERVAL}</kbd></span>
  </h1>						
  <div>
    <button class="display-4">Остановить</button> 
    <button class="display-4">Сбросить</button>
  </div>
</div>

В первой строке к корневому <div> добавили 2 класса Bootstrap: container-fluid(о котором писал выше) и align-items-center – который как раз и выравнивает элементы контейнера по центру.

Далее два <div> с классом display-1 – этот класс как раз для показа крупного текста.

Далее на цифры добавил новый тег <kbd> — который обычно используется для подсветки клавиш, которые нужно нажать. В данном случае он отлично подходит для контраста показываемых цифр.

В последней цифре, показывающей части секунды добавлено условное выражение, позволяющее для однозначных цифр (<10) выводить в начале 0, и не выводить его для двухзначных чисел. Это нужно, чтобы цифры каждую секунду не дёргались. Для этого используем тернарный оператор JavaScript: условие? true: false

Далее в отдельном <div> поместил 2 кнопки с классом Display-4 – этот класс подобрал, как наиболее подходящий по размеру, чтобы кнопки соответствовали размеру таймера. Между кнопками вставил символ   — неразрывный пробел, чтобы кнопки не сливались.

Можно запустить, но кнопки пока не работают. Научим кнопки работать.

Обработка событий


Для начала добавим в код вывода кнопок вызов соответствующих функций:

<button class="display-4" onClick={this.stopTimer}>Остановить</button> 
<button class="display-4" onClick={this.resetTimer}>Сбросить</button>

Обратите внимание, что в React обработчик события onClick, а не onclick, как в JavaScript и вызываемая функция указывается в фигурных скобках без круглых скобок и с указанием объекта, из которого вызывается метод, в данном случае это this.

Теперь определим указанные методы stopTimer() и resetTimer():

stopTimer(){
  clearInterval(this.timerID);
}

resetTimer(){
  this.setState({value: 0});		
}

Но этого ещё недостаточно и если оставить так, то при нажатии кнопки будет появляться ошибка, т.к. this при вызове функции будет undefined. Это возникает из-за того, что в JavaScript, методы класса не привязаны по умолчанию. Как правило, если вы ссылаетесь на метод без () после него, например, onClick={this.resetTimer}, вам необходимо привязать этот метод.

Привяжем методы в конструкторе класса, добавив туда 2 строчки:

this.stopTimer = this.stopTimer.bind(this);
this.resetTimer = this.resetTimer.bind(this);

Отлично, заработало! Вот только кнопкой остановки можно воспользоваться только 1 раз, и после этого кнопки перестают работать. И это логично, ведь вызвав stopTimer() мы отключили регулярный вызов функций, вызвав clearInterval().

В комментариях посоветовали использовать стрелочные функции. Попробовал, это работает. Так что можно можно не добавлять 2 строки в конструктор, а сами функции заменить следующими стрелочными функциями:
stopTimer = () => {
  this.timerID = setInterval(() => this.increment(), 1000/INTERVAL);
}

resetTimer = () => {
  this.setState({value: 0});		
}

Чтобы решить это – сделаем, чтобы кнопка «Остановить» работала ещё и как «Запустить».

Для начала добавим в конструктор булево состояние stopped, чтобы понимать, в каком режиме работает кнопка:

this.state = {value: 0, stopped: false};

Теперь полностью заменяем содержимое метода stopTimer():


  this.setState({stopped: !this.state.stopped});
  if(this.state.stopped){
    clearInterval(this.timerID);
  }
  else
  {
    this.timerID = setInterval(() => this.increment(), 1000/INTERVAL);
  };

В начале метода меняем состояние stopped на противоположное через setState().

Далее, если таймер должен быть остановлен (т.е. stopped = true) – то отключаем регулярный вызов функций через clearInterval(), а если таймер должен быть запущен (т.е. stopped = false), то запускаем регулярный вызов функций аналогично методу componentDidMount().

Также надо исправить метод increment(), чтобы он останавливался, когда stopped = true:
increment(){
  if(!this.state.stopped) (this.setState({value: this.state.value + 1}));
}

И напоследок меняем название кнопки в зависимости от состояния stopped, вставив вместо «Остановить» следующее:

{this.state.stopped?'Продолжить':'Остановить'}

Теперь у нас получился красивый, удобный таймер.

Вместо заключения или вишенка на торте


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

Изменить заголовок можно установив document.title в методе componentDidMount(), но мы пойдём дальше и сделаем, чтобы в заголовке страницы отображалось время с точностью до секунд, для этого добавим установку document.title в специальный метод componentDidUpdate():

componentDidUpdate(){
const value = this.state.value;
if (this.state.stopped) document.title = "Таймер";
else document.title = "Таймер: "+Math.floor(value/INTERVAL/60/60)+":"
+Math.floor(value/INTERVAL/60) % 60+":"+Math.floor(value/INTERVAL) % 60;
}

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

С иконкой всё просто. Достаточно подготовить картинку в формате jpg, bmp, gif, png, закинуть в папку public (а не src, в которой мы в основном работали), назвав, например favicon.png и поменять в файле public\index.html строку:

<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">

на строку:

<link rel="shortcut icon" type="image/png" href="/favicon.png"/>

На сегодня это всё, что я хотел рассказать. В следующей статье подробнее расскажу про Bootstrap, которого в этой статье лишь слегка коснулся. Кроме Bootstrap остались ещё важные темы: списки, таблицы, формы и мышление в стиле React.

Напоследок репозиторий в BitBucket, в котором есть весь код к этой статье

Комментарии (42)


  1. leszla
    02.12.2018 23:00

    Кажется, вы забыли закрывающий тэг

    </b>
    или так надо?)


    1. Lavs Автор
      02.12.2018 23:10

      Там веселее было — в тексте упоминал разные теги, такие как <div> <App> <kbd> — они в итоге не показались, но съели закрывающий </b> — в итоге пришлось выискивать такие места и заменять < и > на соответствующие escape-последовательности)

      Теперь исправил, вроде теперь всё корректно отображается :)


  1. Tantrido
    02.12.2018 23:27

    Начать с React и Bootstrap за 2 дня
    Вот за это большое спасибо — почитаем!


    1. Lavs Автор
      03.12.2018 10:39

      На этой неделе планирую 2ю часть опубликовать, где будет больше про Bootstrap, которого в 1й части лишт слегка коснулся.


  1. altrus
    02.12.2018 23:36

    А без bootstrap-a никак все это не выровнять и размер шрифта не установить?


    1. Lavs Автор
      02.12.2018 23:54

      Да всё можно и без Bootstrap. Просто с Bootstrap это удобнее и красивее:)


      1. altrus
        03.12.2018 08:53

        Кому удобней? Пользователю, которому придется скачивать дополнительный код, размером больший, чем все остальное, и грузить свой браузер парсингом полутора тысяч ненужных ему css правил?


        1. Lavs Автор
          03.12.2018 09:41

          Программисту для удобства разработки. Пользователю для удобства работы. Пользователи в отличие от программистов устроены так, что им важно, чтобы было красиво и приятно глазу и они не задумываются о производительности. Например, я часио наблюдаю, как у людей пользователей одновременно запущено более 10 приложений и открыто более 100 сайтов. Хотя в целях производительности надо было сократить их в идеале до 1, или хотя бы до 3-4.


          Не отрицаю, что приведённый код можно (а для высоконагруженных приложений) нужно оптимизировать — про оптимизацию есть куча статей. Да то же кэширование может помочь. Статья не про это. А про то, как быстро узнать, что это за технология такая React, и можно ли сделать красиво с помощью Bootstrap?


          1. altrus
            03.12.2018 10:11

            У пользователя ваш bootstrap и вытягиваемый им jquery отъедят лишние несколько мегабайт/десятков мегабайт с окна браузера и загрузят процессор. От всех пользователей говорю вам за это большое спасибо. За то, что вместо десятка простых css правил вы подключаете огромный css/js фреймворк, просто потому, что вам так удобно. Сразу же уча таких же как вы начинающих веб-девелоперов не мелочиться.


            1. Lavs Автор
              03.12.2018 10:33

              Предлагаю решать проблемы по мере их поступления. У меня была задача за пару дней изучить React и сделать на его базе красивое Web-прмложение. В постановке задачи


            1. Lavs Автор
              03.12.2018 10:37

              В постановке задачи не было про производительность.


              Если потребуется оптимизировать производительность — это уже отдельная задача. Возможно в этой задаче откажемся от Bootstrap, а может даже и от React. Может вообще о кажемся от сайта и будем делать нативное мобильное приложение с кусками кода на ассемблере)))


            1. Lavs Автор
              03.12.2018 11:09

              Внёс уточнение в начало статьи, чтобы не вводить людей в заблуждение.


  1. Skycaptain
    03.12.2018 06:11

    А jquery для bootstrap зависимость?


    1. Dead_Angel
      03.12.2018 06:45

      вообще да, если открыть документацию бутстрапа это можно увидеть


  1. mayorovp
    03.12.2018 08:03

    Зачем вам js-часть бутстрапа? Она же будет конфликтовать с React! При желании, конечно же, их можно подружить — но это требует куда более вдувчивого подхода к программированию чем тут продемонстрирован.


    1. Lavs Автор
      03.12.2018 08:45

      А можно подробнее? Где именно будет конфликтовать? Я лишь описываю свой первый опыт изучения React с Bootstrap и описать его максимально простыми словами, без глубокого погружения в ООП, производительность и т.д. И что понимаете под более вдумчивым подходом к программированию? Если что, в следующей статье планирую разобрать код на отдельные файлы-компоненты, добавить unit-тесты.


      1. mayorovp
        03.12.2018 09:09
        +1

        Ну вот, например, решили вы сделать через бутстрап Collapse. И пишете:


        <>
          <a className="btn btn-primary" data-toggle="collapse" href="#collapseExample" >Show/Hide</a>
          <div className="collapse" id="collapseExample">
             {this.props.text}
          </div>
        </>

        Но такой компонент может при любом рендере внезапно свернуться, причем в разных версиях React это будет случаться при разных условиях. Кроме того, использование id в компонентах на React само по себе выглядит не очень.


        1. Lavs Автор
          03.12.2018 09:29

          Спасибо за уточнение. Как лучше переписать такой код?


          1. mayorovp
            03.12.2018 09:41
            +1

            Если анимации необязательны — то вот так:


            <>
              <a className="btn btn-primary" onClick={this.toggle}>Show/Hide</a>
              <div className={this.state.show ? 'collapse show' : 'collapse'}>
                 {this.props.text}
              </div>
            </>

            Если анимации нужны — то нужно либо искать готовое решение именно для React, либо начать с чего-то похожего на вот этот код (и быть готовым фиксить его баги):


            class Collapse extends React.PureComponent {
                render() {
                    return <div>{this.props.children}</div>
                }
            
                componentDidMount() {
                    const $this = $(ReactDOM.findDOMNode(this));
                    $this.css({ collapse: true, show: this.props.visible });
                }
            
                componentDidUpdate(prevProps) {
                    const $this = $(ReactDOM.findDOMNode(this));
                    if (this.props.visible && !prevProps.visible)
                        $this.collapse('show');
                    else if (!this.props.visible && prevProps.visible)
                        $this.collapse('hide');
                }
            
                componentWillUnmount() {
                    const $this = $(ReactDOM.findDOMNode(this));
                    $this.collapse('dispose');
                }
            }
            
            // ...
            
            <>
              <a className="btn btn-primary" onClick={this.toggle}>Show/Hide</a>
              <Collapse visible={this.state.visible}>
                 {this.props.text}
              </Collapse>
            </>

            Точнее не скажу, потому что я React за пределами песочницы ни разу не использовал.


            Кстати, именно из-за сложности последнего решения я и говорю, что js-часть бутстрапа начинающим лучше совместно с React не использовать: нужно понимать что этот код вообще делает, и быть готовым написать аналогичную обертку для любого бутстрап-компонента.


            1. Lavs Автор
              03.12.2018 09:43

              Благодарю. Как буду за компьютером попробую и обновлю статью.


            1. Lavs Автор
              03.12.2018 16:04

              Пересмотрел я код в статье, но так и не нашёл, использования Collapse и id. На будущее приму к сведению. Кстати, JS-часть Bootstrap отключил (обновил статью)


              1. mayorovp
                03.12.2018 16:06

                Ну разумеется вы его там не нашли! :-) Претензия-то была к js-части бутстрапа, которую вы подключили, но не использовали. Проблема была в самом факте подключения.


                1. Lavs Автор
                  03.12.2018 16:33

                  js-часть Bootstrap уже отключил, что и отметил в статье:)


  1. Fragster
    03.12.2018 11:57
    +2

    Вот этим меня бутстрап и не устраивает — то, что для него нужен jquery, что при наличии реактивного фреймворка как бы немного глупо. В этом плане больше bulma нравится.


    1. Lavs Автор
      03.12.2018 13:09

      Про bulma не слышал, посмотрю. Сейчас самый распиаренный именно Bootstrap, и в вакансиях именно он указывается — поэтому его и взял.

      А по поводу jQuery пока в официальной документации обнаружил, что jQuery нужен только для JS-части Bootstrap. Сейчас думаю, как выпилить JS из Bootstrap и оставить только CSS от Bootstrap.


      1. Lavs Автор
        03.12.2018 16:04

        Выпилил jQuery, вроде работает:) Статью обновил.


  1. paratagas
    03.12.2018 12:42
    +2

    Можно попробовать использовать React bootstrap . При беглом просмотре я не нашел в зависимостях пакета JQuery.


    1. Lavs Автор
      03.12.2018 13:08
      +1

      Посмотрел React bootstrap — он основан на Bootstrap 3 и всё ещё находится в статусе беты (судя по номеру релиза). Мне хотелось изучить именно Bootstrap 4, где сетка реализована на FloatBox, а в следующей статье я как раз планирую активно использовать эту сетку. А так да, React bootstrap — интересный вариант для оптимизации.


      1. Lavs Автор
        03.12.2018 16:05

        Только сейчас заметил, что написал FloatBox. Конечно-же имел ввиду FlexBox.


  1. Lavs Автор
    03.12.2018 12:51

    Про bulma не слышал, посмотрю. Сейчас самый распиаренный именно Bootstrap, и в вакансиях именно он указывается — поэтому его и взял.


    А по поводу jQuery пока в официальной документации обнаружил, что jQuery нужен только для JS-части Bootstrap. Сейчас думаю, как выпилить JS из Bootstrap и оставить только CSS от Bootstrap.


  1. bohdan4ik
    03.12.2018 12:59
    +1

    > this.stopTimer = this.stopTimer.bind(this);
    > this.resetTimer = this.resetTimer.bind(this);

    Чтобы таким не заниматься, можно определять методы как стрелочные функции

    class MyComponent extends PureComponent {
      state = {
        stopped: false,
      };
    
      stopTimer = () => this.setState({ stopped: true });
    
      render() {
        return (
          <button onClick={this.stopTimer}>Астанавитесь!</button>
        );
      }
    }
    


    > для этого добавим установку document.title в метод render():

    Какой ужас :)

    Здесь лучше подойдёт componentDidUpdate, потому что рендер может вызываться бесчисленное количество раз в зависимости от различных условий и всё, что он должен делать — возвращать новый VDom, за счёт чего «лишний» рендер будет менее дорогим.


    1. Lavs Автор
      03.12.2018 13:04

      Благодарю, попробую заменить стрелочной функцией и перенести определение заголовка в componentDidUpdate. Если всё успешно получится — внесу коррективы в этот пост.


    1. mayorovp
      03.12.2018 14:20

      Но тут важно помнить, что стрелочные функции не дружат с наследованием.


      1. bohdan4ik
        03.12.2018 15:36

        Честно говоря, не могу придумать ни одного кейса, когда в Реакте необходимо наследование, кроме наследования от Component/PureComponent.


        1. mayorovp
          03.12.2018 15:38

          Тем не менее, в Реакте допустимо наследование — а значит, кто-то будет им пользоваться.


        1. Lavs Автор
          03.12.2018 15:44

          Цитата из learn-reactjs.ru/basics/composition-vs-inheritance:
          «В Facebook React используется в тысячах компонентов, но не было обнаружено каких-либо ситуаций, где разработчики рекомендовали бы создавать иерархии наследования компонентов.»


      1. Lavs Автор
        03.12.2018 15:43

        Важное замечание, хотя в React наследование и не нужно, ведь есть композиция — learn-reactjs.ru/basics/composition-vs-inheritance


        1. mayorovp
          03.12.2018 15:49

          Композиция хорошо работает с готовыми компонентами. А для полуфабрикатов с кучей точек расширения альтернатива только хоки (HOC), но от тех все уже так устали, что придумали хуки.


    1. Lavs Автор
      03.12.2018 16:06

      1.Добавил в статью вариант с использованием стрелочной функции для обработки событий
      2.Перенёс установку заголовка в componentDidUpdate()


  1. mayorovp
    03.12.2018 16:14

    Нашел у вас в коде вот такую строчку:


    this.setState({value: this.state.value + 1})

    Здесь вы неявно предполагаете, что между вызовами setState состояние успеет обновиться.
    Так делать опасно: если на странице будет происходить что-нибудь "тяжелое", то ваш таймер начнет пропускать секунды. Казалось бы, это маловероятно, целая секунда же в периоде… Вот только тот сайт, где вы это читаете, умудряется тормозить на страницах с кучей комментариев даже без скриптов, на чистом css.


    Рекомендуемый документацией React вариант выглядит вот так:


    this.setState(state => ({ value: state.value+1 }));


    1. Lavs Автор
      03.12.2018 19:02

      Благодарю, подправлю


  1. hlogeon
    03.12.2018 17:16

    ИМХО, в 2к18 bootstrap не нужен. Тем более с reactJS. Пишем на реакте и тащим jQuery как зависимость(bootstrap), а смысл в этом какой? Хотите красоту? Ну возьмите современный UI-kit, вроде того же blueprintJS

    Не стоит учить «плохому»