Translate

Поиск по этому блогу

Показаны сообщения с ярлыком JSX. Показать все сообщения
Показаны сообщения с ярлыком JSX. Показать все сообщения

воскресенье, 23 декабря 2018 г.

React-Router простое руководство.

Всем привет И сегодня я вам хочу рассказать что такое реакт-роутер.




Работу простого реакт-роутера вы можете увидеть здесь

Итак, давайте начнём сначала и определим, что вообще такое роутер?

Это такой небольшой программный модуль который помогает нам распоряжаться нашими Url - адресами в браузере. Если всё что мы делали ранее было размещено на одной странице и поэтому в роутере смысла никакого не было, то теперь мы можем с помощью роутера разбивать информацию на нашей странице на некоторые такие логические куски. То есть на заглавной странице у нас будет одна информация, на странице контактов - другая. На странице "О нас" - третья и т.д.

Если говорить конкретно о реализации роутера в react приложении, то есть, как минимум три способа как это можно сделать:
  1. - это написать свой собственный роутер.
  2. - использовать стороннюю библиотеку в которой уже реализован роутер, например Ember.js или другие какие-нибудь.
  3. - использовать специальный npm package который называется реакт-роутер.
Поскольку первый вариант нужно очень долго зарабатывать чтобы он был похож на хороший роутер 2-ой вариант немного выбивается из общей картины нашего react приложения, мы будем использовать 3-ий вариант, а именно - npm module react-router.

Само реакт-приложение мы развернем с помощью create-react-app.

В общем поехали!
И так первое что нужно сделать это установить create-react-app, то есть - нашу заготовку для react приложения.

Установка самого React-приложения.

Перейдем на страницу этого модуля facebook/create-react-app и здесь вы можете подчеркнуть всю необходимую для себя информацию.

Ранее, рекомендовалось установить приложение глобально и разворачивать его с созданием нужной папки. Теперь рекомендуют локальную установку в выбранную директорию.

Вы можете делать так, как вам более удобно. Я использую новый вариант установки. Он будет зависеть от версии вашего npm. Чтобы узнать ее, вам нужно открыть терминал или КС(командная строка, окно команд) в Windows. ⊞ Win + R, написать - cmd и нажать Enter.

npm -v


Если вы предпочитаете развернуть проект из глобально установленного, то вы можете просто выполнить команду:

create-react-app name_your_project_folder


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

cd name_your_project_folder


И теперь, чтобы запустить приложение нужно набрать:

npm start




А перейдя по адресу: http://localhost:3000/ вы увидите работающее приложение.

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

Теперь на очереди реакт-роутер.


Устанавливаем react-router

Посмотреть документацию можно по ссылке: ReactTraining/react-router

Поскольку реакт-роутер не входит в стандартный набор, то мы должны установить его вручную.

Для того чтобы это сделать, нужно остановить сервер и перейти в командную строку Ctrl + C -> y и нажать Enter.

Здесь пишем следующее:

npm install react-router --save


и ставим флажок --save чтобы сохранить его package.json зависимости.

Также нам понадобится ещё один дополнительный модуль это react-router-dom, который позволяет нам более легко оперировать с процессом роутинга.

npm install react-router-dom --save


Можно сделать и одной командой:

npm install react-router react-router-dom --save


Теперь можем переходить непосредственно к созданию самого роутера.

Пишем роутер.

Итак, я захожу в папку со своей приложением и чтобы редактировать приложение созданное с помощью create-react-app, нам необходимо редактировать файл App.js и index.js в папке src.

Если мы вернемся на нас нашу страничку, где можно скачать модуль react-router, то здесь вы увидите что они нам демонстрируют два варианта использования данного плагина 1-ый с использованием ES6 и второй это CommonJS. То есть, по сути разница между ними никакой нет.

Если рассмотреть более подробно файл App.js, то вверху мы импортируем сначала react и компонент чтобы не писать React.Component каждый раз при создание нового компонента.

Далее, импортируют Logo для того чтобы создать нашу картинку. Здесь ничего нестандартного. Все это я рассказывал в своих постах о REACT ранее.

Поскольку нам это приложение уже не нужно, то мы удаляем все из файла App.js.

Итак начнем. Чистый файл App.js.
Я сначала импортирую все необходимые нам модули.

import React, { Component } from 'react';

Далее импортируем несколько новых компонентов из библиотеки react-router. Если вы хотите импортировать именно компоненты вам нужно их указывать в фигурных скобках, как Component, выше. Если же нет, то можно писать просто как импорт React.

Импортируем необходимые компоненты из react-router-dom

import { BrowserRouter, Route, Link } from 'react-router-dom';

И так что делают компоненты?

BrowserRouter - тот самый роутер. Он использует HTML5 History API и следит за тем чтобы ваш UI был всегда синхронизирован с тем что написано в адресной строке.

Следующий Route - он отвечает за отображение непосредственного UI И следит за тем чтобы пути совпадали между компонентом и адресной строкой.

и Link - это такая немножко видоизмененная ссылка под react для редиректа на нужную нам страницу.

Также я рекомендую сделать еще одну небольшую вещь:

import createBrowserHistory from 'history/createBrowserHistory';


UPDATE:В последней версии реакт-роутера, рекомендуют импортировать и подключать так:
import { createBrowserHistory } from "history";
const history = createBrowserHistory();




Это нужно для того, чтобы импортировать createBrowserHistory. Дополнительный модуль history устанавливается вместе с установкой react-router. Он нужен он для того чтобы наше джаваскрипт приложение вело свою историю одинаково, независимо от той среды в которой оно находится.

И давайте сразу его заключим некоторую константу.

const history = createBrowserHistory();

Скобки - поскольку эта функция.

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

Я создам самую самую элементарную базовую структуру нашего сайта. При желании Вы можете усложнить и это будет не так сложно сделать.

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

Первая страница которой она у нас будет это страница Home

const Home и используем стрелочные функции, но попрошу заметить, что мы здесь использовали второй раз круглые скобки из-за того чтобы мы не использовали фигурные скобки. Разница между фигурными и обычными скобками заключается в том, что обычные скобки возвращают всего лишь одну строку кода, а фигурные скобки много строк кода.

Но поскольку у нас JSX элемент является по факту одним элементом и занимает одну строку, мы можем использовать смело круглые скобки.

И это будет абсолютно нормально.

Продублирую этот компонент несколько раз и переименуем и вот что получилось:


const Home = () => (
  <div>
    <h2>Home</h2>
  </div>
);

const About = () => (
  <div>
    h<2>About</h2>
  </div>
);

const Contacts = () => (
  <div>
    <h2>Contacts</h2>
  </div>
);



Теперь начинается самое интересное!

Нам необходимо создать наш роутер. Роутер - Это такой же компонент как и все остальные. Приведу код файла App.js полностью, а ниже - пояснения. Создаем его:


import React, { Component } from 'react';

import { BrowserRouter, Route, Link } from 'react-router-dom';
import createBrowserHistory from 'history/createBrowserHistory';

const history = createBrowserHistory();

const Home = () => (
  <div>
    <h2>Home</h2>
  </div>
);

const About = () => (
  <div>
    <h2>About</h2>
  </div>
);

const Contacts = () => (
  <div>
    <h2>Contacts</h2>
  </div>
);

class Menu extends Component {
  render() {
    return (
      <BrowserRouter history={history}>
        <div>
          <ul>
            <li><Link to="/">Home</Link></li>
            <li><Link to="/about">About</Link></li>
            <li><Link to="/contacts">Contacts</Link></li>
          </ul>
          <hr />
          <Route exact path="/" component={Home} />
          <Route exact path="/about" component={About} />
          <Route path="/contacts" component={Contacts} />
        </div>
      </BrowserRouter>
    );
  }
}

export default Menu;



BrowserRouter это есть непосредственно роутер. К нему мы напишем сразу же атрибут history, то есть мы используем константу history для того, чтобы использовать history/createBrowserHistory библиотеку.

Дальше у нас идёт обычный div и в нём уже будут заключительные наши ссылки и пути к ним.

Их поместим внутри списка ul.

Внутри каждого тега li у нас будет находиться компонент Link, который мы подключили вверху файла.

Каждый Link имеет специальный атрибут to и он и отвечает за то, на какую страницу у нас будет идти редирект.

1 -ый у нас будет идти на страницу Home. Обычно её отображает как слэш - "/".

2 -ой нас будет вести на страницу About, и так далее.

Дальше, для визуального разделения навигации и контента страницы, напишем стандартный тег <hr />

Далее, нам необходимо использовать компонент Route, который мы обозначили сверху.

Данный компонент принимает параметр path. Тут мы указываем такой же путь, как мы указали в линках и указан параметр component, который отвечает за рендер именно того компонента который нам нужен.

В данном случае, нам нужен компонент Home По такому же принципу заполняем и другие роуты.

После этого, мы уже можем посмотреть что у нас получилось в браузере, но для начала нам необходимо экспортировать наш компонент.

Чтобы это сделать напишем внизу файла:

export default Menu;

Теперь нужно внести некоторые изменения в файл index.js

import Menu from './App';

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

Мы импортировали Menu, вместо App, и рендерить на страницу - отправляем Menu, а не App.

Можно смотреть браузер.



Вот мы видим наши простенькое структура. У нас есть наши "кнопки-стрелялки" которые мы указали, и рендер нашей странице, за которую отвечает каждый Link.

То есть, если мы находимся по адресу localhost: 3000 - это является адресом страницы Home. Если же мы нажмем на кнопку Аbout, то рендерится компонент Аbout, но как вы уже заметили почему-то наша компонент Home не пропал!



Это происходит потому, что в нашем коде необходимо указать дополнительные параметр - exact.

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

Если мы сейчас придём на нашу страницу, то компонент Home рендерится только тогда, когда мы находимся на нашей главной странице.

Чтобы это наглядно продемонстрировать, я могу дописать exact, например к ссылке About.

Теперь, если я захожу на вкладку About, в адресной строке написано - about, и если я через слэш допишу ещё какую-нибудь абракадабру конечно же такой ссылки нету, то наш компоненты About уже не высвечивается.

Можете сами в этом убедиться.

Но если же я уберу здесь exact то чтобы мы здесь не написали компонент About будет рендерится всё равно.

Об этом следует помнить!

А вообще, обо всех react-router методах и компонентах, вы можете почитать в официальной документации - reacttraining.com.

Всё достаточно адекватно написано и особенно радует что здесь есть примеры, которые можно использовать в реальных проектах. Это очень удобно. Просто копируете их в ваш проект и всё.

Файлы проекта, на этом этапе, вы можете увидеть по адресу: router-react-abc ci -m"React-Router simple" (ci -m - коммит).

Для простоты дальнейшего использования, масштабирования, я решил разделить компоненты. Для этого я создал папку components в папке src и каждый компонент поместил в отдельный файл с соответствующим названием.

Эти компоненты я подключил в файл App.js. Для примера, добавил еще один файл компонента - Blog.js

Файлы проекта, на этом этапе, вы можете увидеть по адресу: router-react-abc ci -m"Split application into components." (ci -m - коммит).

Это всё что я хотел вам сегодня рассказать. До встречи!



                                                                                                                                                             

понедельник, 14 мая 2018 г.

React.js (8) Отладка




Все статьи по React.js



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

Давайте разберемся. что у нас пошло не так и заодно научимся отладке React приложений.

В каждом случае, когда у вас идет то-то не так, вам нужно просто вспомнить как работает React и повторить за ним эти шаги.

В нашем случае, все что у нас произойдет - в какой-то момент по клику (1) на кнопку вызовется collback (2) он поменяет состояние и это должно привести к перестроению виртуального DOM и это внесет изменения в реальный DOM.



Давайте проверим все ли работает у нас так, как задумано?

В первую очередь нас интересует вызывается ли этот collback(2)?

Нам для этого ненужны какие-то специальные средства отладки. просто добавим console.log('---',1); в эту функцию.

следующим этапом, если все пойдет правильно, то у нас вызовется setState() он в любом случае, приведет к перестроению виртуального DOM, и соответственно вызовется метод render().

В него тоже добавим console.log('---',2);

Третье, что может пойти не так - это, теоретически у нас может как-то не так поменяться состояние. В функции render() добавим в console.log('---',2, this.state);

Идем в консоль - Ctrl + Shift + I и видим:



Collback - вызывается, все вызывается и состояние, на самом деле, меняется. Вроде бы все хорошо. Почему же тогда не работает? Давайте проанализируем наш код:

<ArticleList articles={this.state.reverted ?articles.reverse() : articles}/>

В каждый момент времени он получает массив статей. Или перевернутый - ? articles.reverse() или обычный -

: articles

Проблема в том, что метод articles.reverse() не просто возвращает перевернутый массив, но и меняет этот объект по ссылке!. Таким образом, когда мы в следующий момент времени захотим просто передать в ArticleListмассив статей, он уже будет перевернутый.

Чтобы это продемонстрировать мы добавим в вывод консоли (2) articles.map(article => article.id

console.log("---",2, this.state, articles.map(article => article.id))

И для наглядности вынесем действие смены массива ArticleList наверх.


  import React, {Component} from 'react'
  import ArticleList from './ArticleList'
  import articles from '../fixtures.js'
  import 'bootstrap/dist/css/bootstrap.css'

  class App extends Component {
    state = {
      reverted: false
    }
    render(){
      const articlesList = this.state.reverted ?articles.reverse() : articles
      console.log("---",2, this.state, articles.map(article => article.id))
      return (
        <div className="container">
          <div className="jumbotron">
            <h1 className="display-3">
                App name
                <button className="btn" onClick = {this.revert}>Revert</button>
            </h1>
          </div>  
            <ArticleList articles={articlesList}/>
        </div>
      );
    }
      revert = () => {
        console.log("---",1)
       this.setState({
        reverted: !this.state.reverted
      })
    }
  }
  export default App



Давайте посмотрим



Массивы действительно меняются местами, но когда мы нажимаем еще раз, и у нас уже {reverted: false} (2) мы все еще ссылаемся на предыдущий массив с тем же порядком. Соответственно, пока мы его не перевернем в следующий раз. ничего не произойдет.

Попробуем "починить" это "наивным способом".

Добавили articles.reverse()



Таким образом у нас каждый раз будет по клику меняться направление. Даже наш флажок мы оставили без изменений - reverted: !this.state.reverted, потому как он уже ни на что влиять не будет.

В ArticleList мы просто передадим articles. Мы будем их менять по ссылке - <ArticleList articles={articles}/>

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

Обратите внимание на то, что мы делаем! Это ужасно!!! Никогда так не делайте!!!

На самом деле сейчас мы по ссылке меняем массив (на скрине сверху обвел красным), который может использоваться где угодно в нашем приложении. Мы же не знаем где еще мы считаем эти articles. Поэтому правило:

Работайте в компонентах с локальными переменными, которые вы видите где используются и как вы с ними взаимодействуете!


Иначе, может получиться так, что вы поменяете переменную в одном месте, а потом не сможете понять, почему соседний компонент работает не так, как вы это задумали! Поэтому->

Никогда не меняйте по ссылке внешние переменные и тем более то, что приходит вам в props


Теперь давайте попробуем исправить ситуацию.

Давайте, при создании компонента, мы создадим локальную копию этого массива.

articles = articles.slice()

И теперь у нас для каждого компонента App будет своя копия articles. И теперь с копией мы можем работать. Нам нужно только добавить this к изменению порядка в функции reverse()->

this.articles.reverse()

и

<ArticleList articles={this.articles}/>

Теперь мы не будем менять глобальную переменную articles, а меняем только то, что "живет" в this.

Уже лучше! И все казалось бы работает нормально, но здесь мы рискуем столкнуться с еще одной проблемой.

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

import React, {PureComponent} from 'react'

и соответственно -

class App extends PureComponent

И аналогично в файле index.js перепишем ArticleList перепишем с функционального компонента в классовый компонент. И тоже сделаем его PureCompomemt

index.js

  import React, {PureComponent} from 'react'
  import Article from '../Article'
  import './style.css'
  export default class ArticleList extends PureComponent {
    render(){
        const articleElements = this.props.articles.map((article, index) =>
            <li key = {article.id} className="article-list__li">
              <Article article = {article} defaultOpen = {index === 0}/>
            </li>
          )
        return(
            <ul>
              {articleElements}
            </ul>
          );
    }
  }



Посмотрим, как это работает. Идем в приложение. Нажимаем на кнопку Revert и ...ничего не происходит!

Почему так?

Давайте пройдемся по нашему алгоритмы.

В первую очередь - в какой момент времени у нас должно было что-то произойти?

В функции revert (App.js) произошло изменение состояния, значит в render, начнется перестроение виртуального дерева.

Ставим проверочный вывод в консоль в самом верху рендера - console.log("---",1)

Затем, в рамках перестроения общего виртуального дерева будет перестраиваться и ArticlaList (index.js) поставим там - console.log("---",2), так же в самом начале рендера. И теперь посомотрим, что происходит у нас в консоле.



В первый раз (1 на фото) - сработали обе функции, потому что DOM дерево строилось в первый раз.

Потом, мы нажали кнопку Revert и у нас сработала только 1 функция (2 на фото)

Если заменить PureComponent на обычный Component, то будут срабатывать обе функции.

Так происходит потому что с точки зрения ArticleList, когда мы будем проверять props в shouldComponentUpdate(), props у нас не поменяются.

Единственный props , который к нам приходит - this.props.articles

Но articles мы меняем по ссылке. Таким образом это всегда один и тот же массив.Соответственно, каждый раз при престроении мы передаем туда ссылку на один и тот же массив и несмотря на то, что внутренние составляющие его поменялись (порядок). Но с точки зрения ArticleList это все тот же массив. И он будет считать, что перестроение делать ненужно!

Это одни из главных причин почему в React настолько популярна работа с иммутабельными данными.

Иммутабельные - это данные, которые вы не меняете по ссылке

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

Соответственно правильным будет вот такое решение!

App.js

  import React, {PureComponent} from 'react'
  import ArticleList from './ArticleList'
  import articles from '../fixtures.js'
  import 'bootstrap/dist/css/bootstrap.css'

  class App extends PureComponent {
    state = {
      reverted: false
    }
    render(){
      console.log('---',1)
      return (
        <div className="container">
          <div className="jumbotron">
            <h1 className="display-3">
                App name
                <button className="btn" onClick = {this.revert}>Revert</button>
            </h1>
          </div>  
            <ArticleList articles={this.state.reverted ? articles.slice().reverse() : articles}/>
        </div>
      );
    }
      revert = () => {
       this.setState({
        reverted: !this.state.reverted
      })
    }
  }
  export default App



Это, почти также как и было в самом начале, но единственное что - это для того, чтобы вы ничего не меняли по ссылке, чтобы articles оставался такой же как и ранее, мы создали копию (articles.slice()) и переворачивали уже копию. Не меняя при этом оригинальный массив.

Вот теперь у нас все будет хорошо!

Никогда не меняйте ничего из внешних переменных!

Никогда не меняйте ничего, что приходит в props!

Лучше, вообще ничего не менять по ссылке, и каждый раз, нужно какое-то изменение, создайте локальную копию и меняйте уже её!


Все статьи по React.js



Файлы, которые мы изменяли в этот раз:


App.js ( со всеми изменениями последний на странице)

components/ArticleList/index.js

  import React, {PureComponent} from 'react'
  import Article from '../Article'
  import './style.css'
  export default class ArticleList extends PureComponent {
    render(){
      console.log("---",2)
        const articleElements = this.props.articles.map((article, index) =>
            <li key = {article.id} className="article-list__li">
              <Article article = {article} defaultOpen = {index === 0}/>
            </li>
          )
        return(
            <ul>
              {articleElements}
            </ul>
          );
    }
  }



                                                                                                                                                             

воскресенье, 13 мая 2018 г.

React.js (7) shouldComponentUpdate(). Оптимизация.

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


Все статьи по React.js



Давайте посмотрим как ведет себя React без него. В файле Article.js. У нас в этом файле слишком много различных выводов данных в консоль (console.log()).

Часть из них, например - в componentWillReceiveProps(nextProps) и в функции handleClick , мы уберем и посмотрим, что происходит тогда, когда мы хотим поменять порядок статей.

Мы нажимаем на кнопку и у нас семь раз (!!!) перестраивается порядок наших статей component will update.

См.фото ниже.



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

Конечно, в реальном DOM эти изменения не будут отображаться, но тем не менее можно бы было обойтись и без лишних перестроений виртуального DOM. Для этого нам и потребуется shouldComponentUpdate(). Давайте посмотрим как он работает.

Изначально наше виртуальное дерево выглядело так (для первых трех статей):



У нас открыта первая статья и закрыты все остальные.

Мы нажимаем на кнопку Revert и будем перестраивать виртуальный DOM.



Для этого в ArticleList передадим массив в перевернутом порядке и для каждой статьи начнем обновлять виртуальный DOM.

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

См. фото ниже.



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

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



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

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

Единственное что его интересует это как ваше приложение выглядело до обновления и как оно должно выглядеть после. То есть - какие изменения нужно сделать. Однако, мы зная особенности наших компонентов. особенности бизнес-логики можем помочь React и подсказать, что в некоторых случаях перестраивать компоненты необязательно. Для этого нам необходимо реализовать на компоненте метод shouldComponentUpdate()

Предположим. что мы знаем, что на наш компонент сейчас может повлиять только то открыт он или закрыт. То есть состояние isOpen: true / false. Соответственно, мы можем сравнить старое состояние с новым, понять поменялось ли что-либо и принять решение - нужно ли нам перестраивать данный компонент или нет.

Давайте посмотрим как это работает.

При этом ArticleList, когда будет перестраивать виртуальный DOM, он будет "спрашивать" у компонента - нужно ли его перестроить, вызывая на нем shouldComponentUpdate().

Если он вернет true, то он зайдет, сравнит и внесет изменения.

Затем пойдет в следующую статью и вызовет у нее shouldComponentUpdate() и если вернет false, то он просто пропустит ее. Не будет заходить и перестраивать виртуальный DOM и тем более ничего не станет менять в реальном DOM. Он просто пойдет дальше.

Если статья вернет true, то пойдет и поменяет в ней виртуальный DOM и внесет все необходимые изменения в реальную структуру.

см фото ниже



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

Давайте это реализуем.

Идем в файл Article.js


  shouldComponentUpdate(nextProps, nextState) {
    return this.state.isOpen !== nextState.isOpen
  }



Здесь мы будем следить за состоянием (открыто \ закрыто). Функция вернет true или false в зависимости от того, поменялось ли состояние.

Проверяем.



Теперь will update вызывается всего два раза!все равно сколько раз бы мы не меняли порядок. Почему так происходит?. Потому что для "внутренних" статей у нас ничего не меняется.

Но у такой реализации shouldComponentUpdate() есть опасность!

предположим, что у нас появляются какие-либо еще интересующие нас данные. Например: Мы хотели бы посчитать количество кликов по заголовку.

Для этого мы повесили бы на заголовок hendler

Article.js

  import React, {Component} from 'react'

  class Article extends Component {
    constructor(props) {
      super(props)

      this.state = {
        isOpen: props.defaultOpen,
        count: 0
      }
    }
    shouldComponentUpdate(nextProps, nextState) {
      return this.state.isOpen !== nextState.isOpen

    }

    componentWillMount() {
      console.log('---','mounting')
    }

    componentWillReceiveProps(nextProps) {
      if(nextProps.defaultOpen !== this.props.defaultOpen) this.setState({
        isOpen: nextProps.defaultOpen
      })
    }
    componentWillUpdate() {
      console.log('---','component will update')
      
    }

    render() {
        const {article} = this.props
        const style = {width:'50%'}
        const body = this.state.isOpen && <section className="card-text">{ article.text }</section> 
      return(
        <div className="card mx-auto" style={style}>
          <div className="card-header">
            <h2 onClick={this.incrementCounter}>
                { article.title }
                clicked {this.state.count}
                <button onClick={this.handleClick} className="btn btn-primary btn-lg float-right">
                    {this.state.isOpen ? 'close' : 'open'}
                </button>
            </h2>
          </div>
          <div className="card-body"> 
                <h6 className="card-subtitle text-muted">
                   "creation date : "{ (new Date(article.date)).toDateString()}
                </h6>
                { body }
          </div>
        </div>
      );
    }
    incrementCounter = () => {
      this.setState({
        count: this.state.count + 1
      })
    }

    handleClick = () =>{
     
      this.setState({
        isOpen: !this.state.isOpen
      })
    }
  }

  export default Article



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



Почему так происходит? Потому что мы делали предположения только про isOpen

Теперь нам нужно принимать во внимание еще и count. Следить за изменением такой функциональности достаточно неудобно. Поэтому чаще всего shouldComponentUpdate() реализуют другим способом.

Сравнивая все props старый с новыми и все элементы state. и если у нас поменяется хоть одно, то компонент будет перестраиваться. Если нет, то тогда все без изменений.

Этот подход настолько распространенный, что для него есть даже отдельный компонент PureComponent в React (мы его и добавим) в импорт и унаследуем Article от него, то нам не придется реализовывать shouldComponentUpdate(), как ранее.

то есть вот эту часть можно будет убрать.
Article.js

    shouldComponentUpdate(nextProps, nextState) {
      return this.state.isOpen !== nextState.isOpen

    }




Article.js

  import React, {Component, PureComponent} from 'react'

  class Article extends PureComponent {
    constructor(props) {
      super(props)

      this.state = {
        isOpen: props.defaultOpen,
        count: 0
      }
    }

    // shouldComponentUpdate(nextProps, nextState) {
    //   return this.state.isOpen !== nextState.isOpen

    // }

    componentWillMount() {
      console.log('---','mounting')
    }

    componentWillReceiveProps(nextProps) {
      if(nextProps.defaultOpen !== this.props.defaultOpen) this.setState({
        isOpen: nextProps.defaultOpen
      })
    }
    componentWillUpdate() {
      console.log('---','component will update')
      
    }

    render() {
        const {article} = this.props
        const style = {width:'50%'}
        const body = this.state.isOpen && <section className="card-text">{ article.text }</section> 
      return(
        <div className="card mx-auto" style={style}>
          <div className="card-header">
            <h2 onClick={this.incrementCounter}>
                { article.title }
                clicked {this.state.count}
                <button onClick={this.handleClick} className="btn btn-primary btn-lg float-right">
                    {this.state.isOpen ? 'close' : 'open'}
                </button>
            </h2>
          </div>
          <div className="card-body"> 
                <h6 className="card-subtitle text-muted">
                   "creation date : "{ (new Date(article.date)).toDateString()}
                </h6>
                { body }
          </div>
        </div>
      );
    }
    incrementCounter = () => {
      this.setState({
        count: this.state.count + 1
      })
    }

    handleClick = () =>{
     
      this.setState({
        isOpen: !this.state.isOpen
      })
    }
  }

  export default Article




PureComponent отличается от обычного Component тем, что у него уже изначально реализовано - shouldComponentUpdate(nextProps, nextState), который изначально сравнивает все элементы props и все элементы state.

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

Таким образом с помощью компонента shouldComponentUpdate мы можем оптимизировать наше приложение.

Тем не менее этим не следует злоупотреблять!

Как и любая другая оптимизация она должна применяться с умом!

PureComponent - может помочь оптимизировать наше приложение в тех местах, где это необходимо, но если вы будете расставлять его по всему приложение. то вы рискуете получить проблемы. Это возможно если ваш компонент зависит не только от props и state, или какой - либо дочерний элемент зависит не только от props и state, а окажется так, что вы этого не знали, то соответственно можете получить ряд багов, которые потом трудно будет искать и исправлять.

Вывод - используйте PureComponent только по назначению не злоупотребляя им!

Все статьи по React.js



Файлы, которые мы изменяли в этот раз:


Article.js (я его привел полностью со всеми изменениями этого поста выше)
                                                                                                                                                             

суббота, 12 мая 2018 г.

React.js (6) Жизненный цикл компонентов.

Ранее нас интересовали только то, как выглядит наш компонент для тех или иных props или state. Однако, в реальной жизни бывают более сложные задачи, когда нам хочется реагировать на разные события из жизни нашего компонента. Напрмер - его появление, изменения, исчезновение.



Все статьи по React.js



Для этого у компонента существует ряд стандартных методов, которые React вызывает при разнообразных событиях в жизни наших компонентов. Это и есть так называемый жизненный цикл компонентов.

Сегодня мы разберемся с тем как его использовать и где он может быть полезен.

Жизненный цикл реакт компонентов можно условно разделить на три группы.

I. Первая группа - Инициализация.

Инициализация - когда наш компонент будет впервые помещаться вначале в виртуальный, а затем и в реальный DOM.

1) constructor(props)

React для этого вызовет ряд методов жизненного цикла и первый из них - конструктор. - constructor(props).

Мы его уже неявно использовали в Article.js с помощью экспериментального синтаксиса.


  import React, {Component} from 'react'

  class Article extends Component {
state = { isOpen: true }
render() { const {article} = this.props const style = {width:'50%'} const body = this.state.isOpen && <section className="card-text">{ article.text }</section> return( <div className="card mx-auto" style={style}> <div className="card-header"> <h2> { article.title } <button onClick={this.handleClick} className="btn btn-primary btn-lg float-right"> {this.state.isOpen ? 'close' : 'open'} </button> </h2> </div> <div className="card-body"> <h6 className="card-subtitle text-muted"> "creation date : "{ (new Date(article.date)).toDateString()} </h6> { body } </div> </div> ); } handleClick = () =>{ console.log('---', 'clicked') this.setState({ isOpen: !this.state.isOpen }) } } export default Article


На самом деле Babel это перепишет вот таким образом:


  constructor(props) {
    super(props)

    this.state = {
      isOpen: true
    }
  }



Здесь super(props) - это вызов конструктора родительского класса. Это обязательно. А ниже this.state - инициализация состояния. Здесь, теоретически, мы можем сделать состояние компонента (isOpen: true) зависимым от props приходящих в конструктор.

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


  constructor(props) {
    super(props)

    this.state = {
      isOpen: props.defaultOpen
    }
  }


Тогда мы в props можем передать параметр props.defaultOpen. И теперь мы этот параметр будем передавать из Article.js в нашу статью (ArticleList/index.js)


  import React from 'react'
  import Article from '../Article'
  import './style.css'
  export default function ArticleList({articles}) {
        const articleElements = articles.map((article, index) =>
            <li key = {article.id} className="article-list__li">
              <Article article = {article} defaultOpen = {index === 0}/>
            </li>
          )
        return(
            <ul>
              {articleElements}
            </ul>
          );
  }


Таким образом мы сделали открытой по умолчанию (инициализация компонента)только первую статью.



2) Затем вызовется компонент componentWillMount()

C ним мы можем взаимодействовать, реализовав это метод на классе.

Любой из методов жизненного цикла мы должны реализовать - например таким образом:

componentWillMount() {} - чтобы реагировать на соответствующее событие.

Стоит отметить, что методы жизненного цикла доступны исключительно для классовых компонентов ( обьявленые через class - например: class Article extends Component). Они недоступны для упрощенного синтаксиса - (например: function App() ). При реализации компонентов упрощенными методами у них нет ни состояния, ни жизненного цикла!



  import React, {Component} from 'react'

  class Article extends Component {
    constructor(props) {
      super(props)

      this.state = {
        isOpen: props.defaultOpen
      }
    }

componentWillMount() { console.log('---','mounting') }
render() { const {article} = this.props const style = {width:'50%'} const body = this.state.isOpen && <section className="card-text">{ article.text }</section> return( <div className="card mx-auto" style={style}> <div className="card-header"> <h2> { article.title } <button onClick={this.handleClick} className="btn btn-primary btn-lg float-right"> {this.state.isOpen ? 'close' : 'open'} </button> </h2> </div> <div className="card-body"> <h6 className="card-subtitle text-muted"> "creation date : "{ (new Date(article.date)).toDateString()} </h6> { body } </div> </div> ); } handleClick = () =>{ console.log('---', 'clicked') this.setState({ isOpen: !this.state.isOpen }) } } export default Article


Если мы захотим использовать жизненный цикл для таких компонентов, то нам их придется переписать в вид class Article extends Component.

componentWillMount() - часто используется для получения необходимых данных. например - оправка запроса статьи на сервер.

3) Затем, вызывается метод - render()

render() должен быть чистым, т.е не содержать всяких setState, запросов к серверу. Единственная его задача - построить виртуальный DOM нашего компонента.

После построения виртуального DOMа, он будет помещен в реальный DOM и после этого вызовется метод componentDidMount().

4) componentDidMount()

Этим методом мы можем реагировать на появление нашего компонента в реальном DOM, на окончание его инициализации. Например: мы можем получить размеры или позиционирование узла (DOM Node) где он отображается, мы можем подписаться на изменение его данных или повесить собственные listener (слушатели событий) на соответствующие DOM элементы.

II. Обновление

Обновление может происходить по двум причинам. Либо у нас произошел setState() внутри компонента (например изменилось isOpen с true на false) , либо setState() произошел у кого-то из "родителей" компонента. Если setState() произошел у кого-то из "родителей", тогда компонент (например ArticleList) перестраивает собственное дерево, в том числе перестраивает и все дочерние компоненты.

1) componentWillReceiveProps(nextProps)

Когда Reac будет перестраивать виртуальное дерево для статьи Reac вызовет метод для статьи componentWillReceiveProps(nextProps), в который передаст новые проперти с которыми строится виртуальный DOM.

Обратите внимание, что это не означает. что проперти на самом деле поменялись. React не делает никаких предположений о наших данных. Они могли остаться неизменными. Для этого в аргументы передается объект - nextProps, который мы можем сравнить с текущими проперти (this.props).

У этого метода существует два распространенных примеры использования.

Первое - у нас поменялись какие-то важные данные и нам необходимо на это отреагировать. Скажем, пришла новая статья и нам необходимо загрузить для нее данные с сервера.

Второе - если мы завязали наше состояние компонента на проперти. Теперь нам придется следить за их изменениями и возможно приводить состояние State к нужному виду.

Давайте для примера добавим возможность поменять порядок статей.

Для этого перепишем App.js Нам нужно поменять function App на class App extends Component чтобы добавить ему состояние. И добавим кнопку по которой будем менять это состояние.
App.js


  import React, {Component} from 'react'
  import ArticleList from './ArticleList'
  import articles from '../fixtures.js'
  import 'bootstrap/dist/css/bootstrap.css'

  class App extends Component {
    state = {
      reverted: false
    }
    render(){
      return (
        <div className="container">
          <div className="jumbotron">
            <h1 className="display-3">
                App name
                <button className="btn" onClick = {this.revert}>Revert</button>
            </h1>
          </div>  
            <ArticleList articles={this.state.reverted ?articles.reverse() : articles}/>
        </div>
      );
    }
    revert = () => this.setState({
      reverted: !this.state.reverted
    })
  }
  export default App



Теперь при клике по кнопке статьи меняются местами (реверс), но при этом открытой остается одна статья, которая изначально первая.



Так происходит потому что статьи не инициализируются заново. Они просто отображаются в другом порядке. Теперь, не смотря на то, что в "первую статью" (при реверсе) пришло свойство defaultOpen: true, мы не привели состояние к нужному виду.

Для этого мы реализуем метод componentWillReceiveProps(nextProps) и приведем состояние к нужному виду:
Article.js

  import React, {Component} from 'react'

  class Article extends Component {
    constructor(props) {
      super(props)

      this.state = {
        isOpen: props.defaultOpen
      }
    }

    componentWillMount() {
      console.log('---','mounting')
    }

    componentWillReceiveProps(nextProps) {
      if(nextProps.defaultOpen !== this.props.defaultOpen) this.setState({
        isOpen: nextProps.defaultOpen
      })
    }
    render() {
        const {article} = this.props
        const style = {width:'50%'}
        const body = this.state.isOpen && <section className="card-text">{ article.text }</section> 
      return(
        <div className="card mx-auto" style={style}>
          <div className="card-header">
            <h2>
                { article.title }
                <button onClick={this.handleClick} className="btn btn-primary btn-lg float-right">
                    {this.state.isOpen ? 'close' : 'open'}
                </button>
            </h2>
          </div>
          <div className="card-body"> 
                <h6 className="card-subtitle text-muted">
                   "creation date : "{ (new Date(article.date)).toDateString()}
                </h6>
                { body }
          </div>
        </div>
      );
    }
    handleClick = () =>{
      console.log('---', 'clicked')
      this.setState({
        isOpen: !this.state.isOpen
      })
    }
  }

  export default Article



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

Здесь все еще есть небольшой баг (множественного нажатия на кнопку) но мы с ним разберемся позднее.

2) Следующий элемент жизненного цикла - shouldComponentUpdate()

shouldComponentUpdate() - метод, который позволяет нам оптимизировать наше приложение, в ручном режиме управляя тем, нужно ли перестраивать виртуальный DOM для этого компонента или нет.

Подробнее о нем мы поговорим в следующих постах.

После него вызывается...

3) componentWillUpdate(nextProps, nextState)

Этот метод предупреждает нас о том. что сейчас мы будем перестраивать виртуальный DOM для данного копонента. У нас уже есть готовые Props и State , и здесь мы можем аналогичным образом отреагировать на какие-то изменения. Например: если статья у нас была закрыта, а стала открыта т.е. у нас поменялся State isOpen, и мы хотим загрузить текст для данной статьи.

Еще одни важный нюанс.
componentWillReceiveProps(nextProps) будет вызываться исключительно тогда. если у нас перестраивается кто-то из родителей и у нас могли поменяться Props.

shouldComponentUpdate() и componentWillUpdate(nextProps, nextState) будет вызываться независимо от того перестраивается кто-то из родителей, или у нас произошел setState() в нашем компоненте
Поcмотрим на примере Article.js

 componentWillReceiveProps(nextProps) {
     console.log("---",'will receive props')
    if(nextProps.defaultOpen !== this.props.defaultOpen) this.setState({
      isOpen: nextProps.defaultOpen
    })
  }



Добавим ниже:


  componentWillUpdate() {
    console.log('---','component will update')
    
  }



Теперь откроем консоль в нашем приложении. Вы увидите. что когда у нас перестраивается виртуальный дом всего нашего приложения (нажимаем кнопку - Revert) у нас будет вызываться component will receive props и component will update для всех наших статей.



А если мы будем просто открывать и закрывать статья (кнопка статьи Open/Close), то будет вызываться только component will update.



После метода componentWillUpdate(nextProps, nextState) будет вызываться метод render().

4) Метод render()

Перестраивается виртуальное дерево. Сравнивается с прошлым виртуальным деревом. Все необходимые изменения вносятся в реальный DOM и после этого вызывается componentDidUpdate()

5) Метод - componentDidUpdate(prevProps, prevState)

в этом методе есть также доступ к прошлым prevProps и prevState и к современным props и state. Единственное что, так это то, что прошлые теперь будут жить в аргументах функции, а современные - this.props и this.state чаще всего используются если вас интересуют какие-либо составляющие реального DOM. Например: размер вашего компонента в каждый момент времени или его позиционирование на экране.

III. Удаление - смерть компонента

Последний этап в жизни компонента, когда мы будем удалять его из виртуального, а в последствии и из реального DOM.

Единственный метод который здесь вызовется - componentWillUnmount(), который предупредит нас об этом.Это идеальное место для того, чтобы подчистить какие то подписки, возможно, на события в реальном DOM, возможно на изменения данных. Любые подписки которые вы делали и собственно - провести всю логику деструктуризации вашего компонента.

Все статьи по React.js



Файлы, которые мы изменяли в этот раз:



App.js

  import React, {Component} from 'react'
  import ArticleList from './ArticleList'
  import articles from '../fixtures.js'
  import 'bootstrap/dist/css/bootstrap.css'

  class App extends Component {
    state = {
      reverted: false
    }
    render(){
      return (
        <div className="container">
          <div className="jumbotron">
            <h1 className="display-3">
                App name
                <button className="btn" onClick = {this.revert}>Revert</button>
            </h1>
          </div>  
            <ArticleList articles={this.state.reverted ?articles.reverse() : articles}/>
        </div>
      );
    }
    revert = () => this.setState({
      reverted: !this.state.reverted
    })
  }
  export default App




Article.js

  import React, {Component} from 'react'

  class Article extends Component {
    constructor(props) {
      super(props)

      this.state = {
        isOpen: props.defaultOpen
      }
    }

    componentWillMount() {
      console.log('---','mounting')
    }

    componentWillReceiveProps(nextProps) {
       console.log('---','will receive props')
      if(nextProps.defaultOpen !== this.props.defaultOpen) this.setState({
        isOpen: nextProps.defaultOpen
      })
    }
    componentWillUpdate() {
      console.log('---','component will update')
      
    }

    render() {
        const {article} = this.props
        const style = {width:'50%'}
        const body = this.state.isOpen && <section className="card-text">{ article.text }</section> 
      return(
        <div className="card mx-auto" style={style}>
          <div className="card-header">
            <h2>
                { article.title }
                <button onClick={this.handleClick} className="btn btn-primary btn-lg float-right">
                    {this.state.isOpen ? 'close' : 'open'}
                </button>
            </h2>
          </div>
          <div className="card-body"> 
                <h6 className="card-subtitle text-muted">
                   "creation date : "{ (new Date(article.date)).toDateString()}
                </h6>
                { body }
          </div>
        </div>
      );
    }
    handleClick = () =>{
      console.log('---', 'clicked')
      this.setState({
        isOpen: !this.state.isOpen
      })
    }
  }

  export default Article




index.js

  import React from 'react'
  import Article from '../Article'
  import './style.css'
  export default function ArticleList({articles}) {
        const articleElements = articles.map((article, index) =>
            <li key = {article.id} className="article-list__li">
              <Article article = {article} defaultOpen = {index === 0}/>
            </li>
          )
        return(
            <ul>
              {articleElements}
            </ul>
          );
  }



                                                                                                                                                             

среда, 9 мая 2018 г.

React.js (2) Разбиваем на компоненты.

Хранить весь код в одном файле index.js неудобно и поэтому мы разобьем наш файл на компоненты.

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



Каждый компонент может быть повторно использован и представляет собой некую отдельную, самостоятельную часть (можно представить как строительный блок) кода. Будет правильным поместить их в отдельные файлы. Поэтому в папке src мы создадим для всех компонентов отдельную папку - components. В ней создадим два файла - Article.js и App.js.

В них мы поместим код (функцию) наших компонентов из файла index.js.

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

Для этого мы пропишем строку import React from 'react' в самом начале файлов Article.js и App.js.

Теперь нам нужно произвести подключение наших компонентов. Для этого под функциями в файлах компонентов мы сделаем export наших компонентов. Например так:

export default App - для App - компонента.

export default Article - для Article - компонента.

В компонент App нам нужно импортировать код компонента Article, так как он принимает участие в строительстве самого App - компонента. вспоминаем:

function App() {
 return (
    <div>
        <h1>App Name</h1>
        <Article/>
    </div>
  );
}



Именно поэтому мы в фале App.js пишем - import Article from './Article'

После этих изменений стоит запустить сервер (в КС (bash) - npm start. см. Здесь! и убедиться, что все продолжает работать!

На данном этапе наш код компонента Article показывает статические названия, которые мы прописали внутри тегов title, body и т.д. Но нам нужно сделать таким образом, чтобы туда автоматически подставлялись текст, заголовок и прочие необходимые атрибуты статьи.

Для этого мы можем использовать js - переменные ( внутри фигурных скобок - { js-variable } ).

Наши компоненты (App и Article) это обычные функции и им в аргументы мы можем передавать нужные данные. Все данные будут приходить в первом аргументе и его принято называть props.

Коль скоро мы хотим получить в компоненте Article статью, то мы можем создать переменную const {article} = props - используем деструктуризацию объекта и все эти переменные мы сможем использовать внутри JSX.({ article.title }, { article.text } и пр.)

Файл Article.js

import React from 'react'

function Article(props) {
  const {article} = props
  const body = <section>{ article.text }</section> 
 return(
      <div>
        <h2>{ article.title }</h2> 
        { body }
        <h3>
         "creation date : "{ (new Date(article.date)).toDateString()}
        </h3>
      </div>
    );
}

export default Article



Файл App.js

import React from 'react'
import Article from './Article'

import articles from '../fixtures.js'

function App() {
 return (
    <div>
        <h1>App Name</h1>
        <Article article={articles[0]}/>
    </div>
  );
}

export default App



Здесь стоит пояснить, что строкой import articles from '../fixtures.js' мы подключаем файл в котором будут храниться сами статьи.

Для того, чтобы передать наши статьи article={articles[0]}
Файл fixtures.js

export default [
    {
        "id": "56c782f18990ecf954f6e027",
        "date": "2016-06-09T15:03:23.000Z",
        "title": "React Is Awesome!!!",
        "text": "React makes it painless to create interactive UIs. Design simple views for each state in your application, and React will efficiently update and render just the right components when your data changes. \n We don't make assumptions about the rest of your technology stack, so you can develop new features in React without rewriting existing code. Build encapsulated components that manage their own state, then compose them to make complex UIs. Since component logic is written in JavaScript instead of templates, you can easily pass rich data through your app and keep state out of the DOM.",
        "comments": [
            {
                "id": "qwerqwer",
                "user": "Gilliam Underwood",
                "text": "Velit anim deserunt elit velit est fugiat duis eiusmod eu do incididunt ut tempor voluptate. Officia dolor aliqua id anim mollit pariatur id commodo. Laborum minim non ut aliquip commodo est consectetur. Mollit eu aliqua tempor est nulla ullamco irure. Sit non amet et eiusmod cillum ex cillum anim incididunt ad laboris mollit. Sunt quis incididunt elit ea qui non ullamco aliquip consequat voluptate eiusmod est. Irure laboris amet culpa sit aliquip."
            },
            {
                "id": "lkjhsdlfkg",
                "user": "Dolly Franklin",
                "text": "Aliquip id nostrud adipisicing irure. Labore reprehenderit ea ex officia ullamco incididunt consequat elit amet quis commodo. Fugiat amet veniam cillum ut aliquip velit est esse minim fugiat eiusmod sint. Commodo ea in culpa deserunt."
            },
            {
                "id": "zxcvzcxvzxcv",
                "user": "Brennan Atkins",
                "text": "Nisi sit nisi cillum dolor fugiat sint do nostrud ex cillum cupidatat. Culpa do duis non et excepteur labore dolor culpa qui tempor veniam. Ex labore deserunt qui sit aute ad incididunt do cupidatat eiusmod reprehenderit ad. Qui laborum qui voluptate velit et consectetur ipsum enim dolore minim. Est sint velit tempor reprehenderit. Qui consectetur ad minim consequat."
            },
            {
                "id": "fghjfghjfghj",
                "user": "Jodi Humphrey",
                "text": "Non amet amet ut magna culpa dolore consequat occaecat. Commodo adipisicing laboris voluptate enim mollit mollit anim aliquip deserunt nostrud deserunt cillum est. Ad eu cupidatat dolor nostrud et minim id in dolor occaecat ad magna elit. Laboris elit laboris aliquip Lorem reprehenderit id amet reprehenderit laborum minim incididunt cupidatat eiusmod."
            },
            {
                "id": "ertyoertywte",
                "user": "Joyce Weber",
                "text": "Non qui Lorem qui commodo sint in esse non aliqua pariatur mollit veniam. Elit labore ad nisi anim adipisicing tempor velit commodo adipisicing ipsum ut. Nostrud cillum aliquip adipisicing id do occaecat est eiusmod adipisicing duis. Magna dolore et non nisi in non cillum officia elit non esse proident irure aute. Proident mollit amet enim dolore eiusmod dolor qui. Eiusmod reprehenderit cillum sit deserunt nostrud enim duis excepteur. Excepteur pariatur sunt in ipsum id minim est mollit."
            }
        ]
    },
    {
        "id": "56c782f17b4e0ba78c7ad717",
        "date": "2016-04-09T18:03:23.000Z",
        "title": "Quis occaecat",
        "text": "Quis occaecat duis aliqua reprehenderit excepteur nisi deserunt excepteur elit magna. Magna cillum anim veniam deserunt voluptate occaecat irure fugiat laboris proident. Tempor do magna deserunt cillum laborum cillum ut.\n\nEst sunt cupidatat in deserunt sit aliqua duis. Mollit consequat duis aliquip occaecat pariatur non do eiusmod dolore amet deserunt ullamco. Ea minim tempor exercitation do tempor nostrud dolor minim veniam laboris commodo ex duis. Do nostrud voluptate ullamco consequat anim tempor voluptate incididunt aliqua tempor.\n\nIn irure quis nostrud do. Labore laboris irure culpa reprehenderit pariatur laboris in commodo culpa enim cillum magna. Magna ipsum pariatur sunt in reprehenderit ipsum duis officia voluptate adipisicing ad officia. Duis est sint mollit amet laborum magna non quis nulla ipsum in veniam sit. Amet velit consequat esse esse ea. Ipsum non do ut cillum in adipisicing labore non commodo do laborum sunt.",
        "comments": [
            {
                "id": "qwerqwertyy",
                "user": "Gilliam Underwood",
                "text": "Velit anim deserunt elit velit est fugiat duis eiusmod eu do incididunt ut tempor voluptate. Officia dolor aliqua id anim mollit pariatur id commodo. Laborum minim non ut aliquip commodo est consectetur. Mollit eu aliqua tempor est nulla ullamco irure. Sit non amet et eiusmod cillum ex cillum anim incididunt ad laboris mollit. Sunt quis incididunt elit ea qui non ullamco aliquip consequat voluptate eiusmod est. Irure laboris amet culpa sit aliquip."
            },
            {
                "id": "sdfgsdfghu7u7urtv",
                "user": "Jodi Humphrey",
                "text": "Non amet amet ut magna culpa dolore consequat occaecat. Commodo adipisicing laboris voluptate enim mollit mollit anim aliquip deserunt nostrud deserunt cillum est. Ad eu cupidatat dolor nostrud et minim id in dolor occaecat ad magna elit. Laboris elit laboris aliquip Lorem reprehenderit id amet reprehenderit laborum minim incididunt cupidatat eiusmod."
            }
        ]
    },
    {
        "id": "56c782f1978fdf9a0100df52",
        "date": "2016-06-02T11:03:23.000Z",
        "title": "Hello my new world",
        "text": "Culpa dolor deserunt veniam irure amet officia excepteur labore nisi labore ad labore laborum aute. Ea irure sit exercitation ex. Aliquip dolore duis ullamco labore qui. Et anim irure laborum quis ipsum. Adipisicing culpa est ea velit veniam dolor nisi. Sit cupidatat velit commodo eu.\n\nUt nulla ut irure cillum irure sint sunt cupidatat tempor laboris incididunt elit occaecat fugiat. Reprehenderit enim cupidatat consectetur pariatur ad pariatur consequat veniam do fugiat Lorem laborum do velit. Nulla aute magna magna nisi officia et. Aute adipisicing eu eiusmod tempor exercitation sint non enim laboris dolor adipisicing.\n\nEa do sint pariatur voluptate ad culpa irure. Cillum pariatur deserunt fugiat elit. Exercitation labore amet deserunt magna. Velit veniam aute officia aliqua ipsum veniam pariatur. Aliquip ullamco fugiat officia non sunt ad consequat ipsum est esse commodo reprehenderit. Ad quis consectetur est exercitation fugiat eiusmod. Laborum reprehenderit esse qui irure.",
        "comments": [
            {
                "id": "kjfviudfv089w74",
                "user": "Joyce Weber",
                "text": "Non qui Lorem qui commodo sint in esse non aliqua pariatur mollit veniam. Elit labore ad nisi anim adipisicing tempor velit commodo adipisicing ipsum ut. Nostrud cillum aliquip adipisicing id do occaecat est eiusmod adipisicing duis. Magna dolore et non nisi in non cillum officia elit non esse proident irure aute. Proident mollit amet enim dolore eiusmod dolor qui. Eiusmod reprehenderit cillum sit deserunt nostrud enim duis excepteur. Excepteur pariatur sunt in ipsum id minim est mollit."
            },
            {
                "id": "r23uyrghasdfb7",
                "user": "Joyce Weber",
                "text": "Non qui Lorem qui commodo sint in esse non aliqua pariatur mollit veniam. Elit labore ad nisi anim adipisicing tempor velit commodo adipisicing ipsum ut. Nostrud cillum aliquip adipisicing id do occaecat est eiusmod adipisicing duis. Magna dolore et non nisi in non cillum officia elit non esse proident irure aute. Proident mollit amet enim dolore eiusmod dolor qui. Eiusmod reprehenderit cillum sit deserunt nostrud enim duis excepteur. Excepteur pariatur sunt in ipsum id minim est mollit."
            }
        ]
    },
    {
        "id": "56c782f1e17f4f9311dfaa2c",
        "date": "2016-05-19T23:03:23.000Z",
        "title": "Voluptate est officia",
        "text": "Voluptate est officia pariatur tempor labore consequat consequat consectetur culpa enim. Amet laborum commodo in ullamco magna aliquip. Cupidatat veniam adipisicing labore irure nostrud sunt proident aute in. Sit Lorem consequat cillum enim id excepteur aliqua nostrud sint quis aute est est dolor. Sunt culpa est magna ad qui consequat exercitation. Enim sit ex do cupidatat laborum.\n\nOccaecat do tempor ex in dolor cupidatat do quis laboris anim. Et duis esse pariatur magna ex ipsum ut do ut id cillum. Nostrud fugiat sit minim ea. Nisi nulla sunt et et voluptate. Velit reprehenderit enim nulla mollit dolore excepteur Lorem do Lorem do cillum est qui. Ipsum mollit duis laboris incididunt non sit sunt proident. Aute voluptate id sunt aute proident officia.\n\nEnim ut nulla cillum officia cupidatat eiusmod amet laborum do labore id. Laborum in laboris aliquip anim consectetur magna reprehenderit consequat eiusmod esse Lorem consequat aliqua. Eiusmod voluptate mollit sint consectetur eu deserunt laboris ad et sunt nulla fugiat quis non. Dolor qui do cupidatat proident mollit amet sint pariatur non aliquip enim ex.",
        "comments": [
            {
                "id": "rflsbvyeuwr334rg5",
                "user": "Random Name",
                "text": "Non qui Lorem qui commodo sint in esse non aliqua pariatur mollit veniam. Elit labore ad nisi anim adipisicing tempor velit commodo adipisicing ipsum ut. Nostrud cillum aliquip adipisicing id do occaecat est eiusmod adipisicing duis. Magna dolore et non nisi in non cillum officia elit non esse proident irure aute. Proident mollit amet enim dolore eiusmod dolor qui. Eiusmod reprehenderit cillum sit deserunt nostrud enim duis excepteur. Excepteur pariatur sunt in ipsum id minim est mollit."
            },
            {
                "id": "234faasffasgfq3r3eda",
                "user": "Felix Soyferman",
                "text": "Non qui Lorem qui commodo sint in esse non aliqua pariatur mollit veniam. Elit labore ad nisi anim adipisicing tempor velit commodo adipisicing ipsum ut. Nostrud cillum aliquip adipisicing id do occaecat est eiusmod adipisicing duis. Magna dolore et non nisi in non cillum officia elit non esse proident irure aute. Proident mollit amet enim dolore eiusmod dolor qui. Eiusmod reprehenderit cillum sit deserunt nostrud enim duis excepteur. Excepteur pariatur sunt in ipsum id minim est mollit."
            },
        ]
    },
    {
        "id": "56c782f197fe2bad471b3740",
        "date": "2016-06-04T15:03:23.000Z",
        "title": "Ex sunt sunt aliqua",
        "text": "Ex sunt sunt aliqua reprehenderit aliqua occaecat. Minim anim commodo officia veniam proident aute cillum eu sunt aute nostrud. Laboris fugiat velit ut pariatur occaecat adipisicing pariatur occaecat. Duis sint enim et consectetur quis pariatur laborum excepteur. Ipsum aliquip qui laborum commodo consectetur do velit veniam occaecat. Ad nisi dolor cillum elit magna dolor voluptate ea. Enim in duis ea consequat sunt Lorem aute.\n\nEst elit sunt quis officia reprehenderit do elit commodo eiusmod esse voluptate. Sit ipsum commodo sint voluptate culpa labore elit magna ullamco nostrud. Laboris magna magna anim labore mollit irure voluptate. Aute non magna aliqua aliqua sunt. Velit mollit consectetur aliqua id tempor ut. Tempor cupidatat aliquip excepteur occaecat incididunt nulla Lorem sint.\n\nNon commodo anim deserunt in et aliquip incididunt ut consectetur sunt esse commodo deserunt et. Tempor fugiat laboris cillum laboris labore. Deserunt quis ad do labore amet culpa officia. Esse et officia nostrud tempor occaecat officia anim incididunt amet sunt excepteur Lorem. Aute occaecat magna velit nisi sit anim consequat velit qui pariatur. Esse incididunt id officia aliqua anim ut et.",
        "comments": [
            {
                "id": "23rfasdfasdvg",
                "user": "Hello World",
                "text": "Non qui Lorem qui commodo sint in esse non aliqua pariatur mollit veniam. Elit labore ad nisi anim adipisicing tempor velit commodo adipisicing ipsum ut. Nostrud cillum aliquip adipisicing id do occaecat est eiusmod adipisicing duis. Magna dolore et non nisi in non cillum officia elit non esse proident irure aute. Proident mollit amet enim dolore eiusmod dolor qui. Eiusmod reprehenderit cillum sit deserunt nostrud enim duis excepteur. Excepteur pariatur sunt in ipsum id minim est mollit."
            },
            {
                "id": "fqasfd4r35tga",
                "user": "Sofia Michailenko",
                "text": "Non qui Lorem qui commodo sint in esse non aliqua pariatur mollit veniam. Elit labore ad nisi anim adipisicing tempor velit commodo adipisicing ipsum ut. Nostrud cillum aliquip adipisicing id do occaecat est eiusmod adipisicing duis. Magna dolore et non nisi in non cillum officia elit non esse proident irure aute. Proident mollit amet enim dolore eiusmod dolor qui. Eiusmod reprehenderit cillum sit deserunt nostrud enim duis excepteur. Excepteur pariatur sunt in ipsum id minim est mollit."
            }
        ]
    },
    {
        "id": "56c782f1a2c2c3268ddb3206",
        "date": "2016-06-08T19:03:23.000Z",
        "title": "Commodo laborum sit nostru",
        "text": "Commodo laborum sit nostrud reprehenderit cupidatat officia laboris. Ipsum minim culpa in enim. Voluptate dolor ea irure nisi incididunt enim magna.\n\nCupidatat quis cillum velit culpa tempor esse irure nostrud ea consectetur officia fugiat irure qui. Enim quis officia do in. Velit veniam ipsum consequat aliqua duis voluptate. Minim nisi ex aute ad.\n\nNisi Lorem ex tempor adipisicing labore. Quis occaecat fugiat pariatur labore culpa cillum laboris. Labore occaecat ut laborum sit ex do sit. Deserunt consectetur elit aute laboris est deserunt officia ullamco sit laboris officia aliquip. Aliqua ut sunt nostrud voluptate excepteur quis incididunt Lorem ut.",
        "comments": [
            {
                "id": "23qwfasdf3",
                "user": "Brad McKeon",
                "text": "Non qui Lorem qui commodo sint in esse non aliqua pariatur mollit veniam. Elit labore ad nisi anim adipisicing tempor velit commodo adipisicing ipsum ut. Nostrud cillum aliquip adipisicing id do occaecat est eiusmod adipisicing duis. Magna dolore et non nisi in non cillum officia elit non esse proident irure aute. Proident mollit amet enim dolore eiusmod dolor qui. Eiusmod reprehenderit cillum sit deserunt nostrud enim duis excepteur. Excepteur pariatur sunt in ipsum id minim est mollit."
            },
            {
                "id": "dadsafw4f45w",
                "user": "Dolly Franklin",
                "text": "Aliquip id nostrud adipisicing irure. Labore reprehenderit ea ex officia ullamco incididunt consequat elit amet quis commodo. Fugiat amet veniam cillum ut aliquip velit est esse minim fugiat eiusmod sint. Commodo ea in culpa deserunt."
            },
            {
                "id": "234erasfdastw4a",
                "user": "Brennan Atkins",
                "text": "Nisi sit nisi cillum dolor fugiat sint do nostrud ex cillum cupidatat. Culpa do duis non et excepteur labore dolor culpa qui tempor veniam. Ex labore deserunt qui sit aute ad incididunt do cupidatat eiusmod reprehenderit ad. Qui laborum qui voluptate velit et consectetur ipsum enim dolore minim. Est sint velit tempor reprehenderit. Qui consectetur ad minim consequat."
            }
        ]
    },
    {
        "id": "56c782fghgfc2c3268ddb3206",
        "date": "2016-06-08T19:03:23.000Z",
        "title": "Lorem Ipsum dolor",
        "text": "Commodo laborum sit nostrud reprehenderit cupidatat officia laboris. Ipsum minim culpa in enim. Voluptate dolor ea irure nisi incididunt enim magna.\n\nCupidatat quis cillum velit culpa tempor esse irure nostrud ea consectetur officia fugiat irure qui. Enim quis officia do in. Velit veniam ipsum consequat aliqua duis voluptate. Minim nisi ex aute ad.\n\nNisi Lorem ex tempor adipisicing labore. Quis occaecat fugiat pariatur labore culpa cillum laboris. Labore occaecat ut laborum sit ex do sit. Deserunt consectetur elit aute laboris est deserunt officia ullamco sit laboris officia aliquip. Aliqua ut sunt nostrud voluptate excepteur quis incididunt Lorem ut."
    }

]


Теперь наш сайт будет выглядеть так.



В props мы можем передавать все что угодно. Это объект, который может передавать в компонент данные. Например

<Article article={articles[0] foo="bar"}/> - передаем строку и нам не нужно заворачивать ее в фигурные скобки.

<Article article={articles[0] flag={ true }}/> передали флажок. Можно передать просто flag без значений и он передастся как true Таким образом мы разбили наше приложение на компоненты и связали их между собой. Подключили вывод текста одной статьи. Продолжение знакомства с React.js смотрите в следующей части - React.js (3) Там мы будем добавлять интерактивность нашим компонентам. Поставим кнопку с изменяющимся текстом для открытия\скрытия статьи.
Все статьи по React.js

                                                                                                                                                             


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com