Как создать в React формы работа с компонентами, примеры кода


Содержание материала:

React: Компоненты и свойства

Компоненты в React являются независимыми, многократно используемыми частями пользовательского интерфейса. Типичная веб-страница может состоять из панели навигации, области содержимого и нижнего колонтитула. В React мы создаем эти области как компоненты (которые, в свою очередь, могут состоять из других компонентов!). Таким образом мы не дублируем код и, как мы увидим, обеспечивает большую гибкость.

Можно думать о компоненте как о функциям JavaScript. Вместо получения аргументов, она получает «Props», а затем возвращают элемент React, чтобы построить то, что мы видим на экране!

Компоненты

На самом деле в React — все является компонентом! Даже стандартные теги HTML являются компонентами, они встроены и добавлены по умолчанию.

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

Здесь мы использовали JSX для вставки I’m a component! в элемент с идентификатором myapp . Наш компонент h1 предоставляется React.DOM . Вы можете проверить весь список, набрав React.DOM в консоли браузера.

Создание пользовательских компонентов

Это замечательно, но как мы создаем наши собственные компоненты? React дает нам возможность создавать пользовательские интерфейсы, составляя наши собственные пользовательские компоненты.

Мы можем определить компоненты двумя способами, давайте теперь посмотрим на каждый из них:

Функциональные компоненты

Компоненты функций на самом деле являются просто функциями JavaScript:

Что делает эту функцию компонентом React, так это то, что она принимает «Props» (или свойства) в качестве аргумента с данными, а затем возвращает элемент React.

Компоненты класса

Классы ES6 также можно использовать для создания компонентов:

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

До недавнего времени в мире React компоненты классов мы использовали чаще — поскольку компоненты класса позволяли определять компоненты с их собственным состоянием (я буду писать о состоянии в моей следующей статье!).

Однако, с появлением React Hooks, функциональные компоненты теперь стали намного мощнее, чем раньше, и мы можем увидеть, что эта тенденция снова изменилась.

React Hooks выходят за рамки этой статьи! Итак, давайте продолжим с компонентами и свойствами.

Компоненты рендеринга

Мы можем отобразить наши элементы, которые представляют теги DOM:

И мы также можем визуализировать наши элементы с помощью пользовательских компонентов:

Когда элемент содержит определенный пользователем компонент, он передает атрибуты JSX компоненту как объект. В React этот объект является тем, что мы называем «Props».

Свойства

Итак, «props» — это то, как наши компоненты приобретают свои свойства.

Давайте посмотрим на это в действии:

Этот код будет отображать на странице «Hello, Bruce!».

Что здесь происходит?

  • ReactDOM.render() вызывается с элементом .
  • React вызывает компонент Greet и передает свойства .
  • Наш компонент Greet возвращает элемент

Hello, Bruce!

Имена компонентов всегда начинаются с заглавной буквы! React рассматривает компоненты, начинающиеся со строчных букв, как теги DOM.

Свойства в функциональных компонентах

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

При использовании функционального компонента все, что передается, — это свойства, они доступны путем добавления props в качестве аргумента функции:

Свойства в классе компонента

В классе компонента свойства передаются по умолчанию. Они доступны как this.props в экземпляре класса.

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

Немного подробнее

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

Props по умолчанию

Если какие-либо значения отсутствуют при инициализации компонента, нам нужно указать значение по умолчанию. Сделать это можно например, так:

Передача свойств

Когда мы инициализируем компонент, мы передаем наши свойства следующим образом:

Если мы работаем со строками, мы можем передать наш props в виде строки (как мы это делали выше с помощью ‘title’. В противном случае мы используем переменные, как мы это сделали с приведенным выше описанием для desc .

Свойства для потомков

Свойство children немного отличается от других. Оно содержит значение всего, что передается в body , например:

В этом примере внутри BlogPostInfo мы можем получить доступ к «More words» через this.props.children .

Компоненты в Компонентах

Компоненты могут включать другие компоненты в свои выходные данные.

Достаточно создать компонент MyApp , который рендерит Greet несколько раз:

Свойства только для чтения!

Независимо от того, объявлен ли ваш компонент как функция или класса, он никогда не сможет изменять свои собственные свойства. Смотрите следующий пример:

Это «чистая» функция, так как она не пытается изменить свои входные параметры и всегда будет возвращать один и тот же результат для одних и тех же параметров.

«Нечистая» функция — это функция, которая меняет свои собственные входные параметры:

Каждый компонент должен действовать как чистая функция по отношению к своим свойствам.

Моя следующая статья будет посвящена «состоянию» в React. С состоянием наши компоненты могут изменять свои выходные данные в ответ на триггеры, такие как действия пользователя или сетевые ответы — без нарушения этого правила.

Заключение

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

Примеры на React. Быстрый старт в изучении React

React строится вокруг концепции компонентов. В отличие от таких фреймворков типа Angular и Ember которые используют двухстороннюю привязку данных для обновления HTML страницы. На мой взгляд, React более прост в освоении, чем Angular и Ember – он намного меньше и хорошо работает с jQuery и другими фреймворками. Он также очень быстрый, потому что он использует виртуальный DOM и синхронизирует только измененные части с основной страницы (доступ к DОМ по-прежнему является медленной часть современного веб-приложения). Однако, обратной стороной является то, что те вещи, которые легко могли бы быть сделаны с помощью привязки данных, занимают немного больше кода в React. В этом вы можете убедиться в приведенных ниже примерах.

Как начать использовать React

Для использования React, вам необходимо включить один файл JavaScript в вашем страницы. Facebook любезно предоставляет нам CDN, на который вы можете сослаться:

Размер файла browser.min.js превышает 1Mb, поэтому такое использование на продакшн строго не рекомендуется!

Это дает вам доступ к глобальному React-объекту, который содержит ряд полезных методов, некоторые из которых вы можете увидеть в наших примерах. Рекомендуемым способом написания реактивного веб-приложения является использование языка под названием JSX. Это немного расширенная версия JavaScript, которая позволяет инициализировать React-компоненты с помощью синтаксиса HTML непосредственно в коде. Этот код компилируется в JavaScript, перед тем как быть интерпретированным браузером. Но, достаточно говорить, давайте перейдем к примерам на React!

Таймер на React

Как я уже упоминал, приложение на React разбивается на блоки — компоненты. Они создаются путем вызова React.createClass() с объектом опций и методов. Каждый компонент имеет состояние (объект с данными) и каждый отвечает за свой собственной рендеринг — метод render() вызывается всякий раз, когда состояние изменяется. Вот пример создания простого таймера:

Таймер на React JS

Надпись с информацией будет выводиться в div с , так что не забудьте разместить его на вашей web-странице. Результат работы нашего примера вы можете посмотреть на странице Таймер на React.

Навигационное меню на React JS

Давайте посмотрим, как мы можем обработать событие click в React на примере создания меню навигации:

Обратите внимание на атрибут className, которого не существует в HTML. дело в том, что JSX — это JavaScript и такие атрибуты, как class и for неодобряются. Вместо них следует использовать className и htmlFor. Результат работы нашего примера вы можете посмотреть на странице Меню на React.

Мгновенный поиск

Как вам, должно быть, известно, больше всего на свете пользователи не любят долгое ожидание. Вот как можно сеализовать на React функционал для мгновенного поиска.

Обратите внимание, что в React обновление состояния нашего контроллера влияет на HTML код, а не наоборот. В этом примере, как только вы ввели какое-то значение текстового поля, оно будет оставаться прежним. Нажатие на клавиши не будет учитываться до тех пор, пока вы не измените текстовоое поля событием onChange (вы можете изменить такое поведение, но это рекомендованный для подобных вещей метод).

Результат работы нашего примера вы можете посмотреть на странице Мгновенный поиск на React JS.

Форма заказа на React

Настоящая мощь React JS проявляется, когда вы работаете с несколькими компонентами. Он позволяет лучше структурировать код и ввести разделение задач, что дает возможность легко использовать ваш код между различными частями приложения. Вот пример формы заказа, которая позволяет клиентам рассчитать стоимость работ за разработку сайта:

Основная проблема, возникающая при работе с несколькими компонентами, — это как реализовать обмен данными между ними. Можно передавать необходимые данные атрибутами при их инициализации. Это работает только в случае общения компонентов «от родительского к дочернему». Для общения в другом направлении можно передать один из методов родительского компонента в дочерний с помощью атрибута.

Результат работы нашего примера вы можете посмотреть на странице Форма заказа на React JS.

Работа с jQuery и AJAX

Этот пример покажет как вы можете объединить React с jQuery и как загружать результаты при через AJAX. В то время как фреймворки типа Angular имеют свои собственные подходы для работы с AJAX, React позволяет вам использовать те библиотеки которые вы хотите.

Обратите внимание: в нашем примере используется один и тот же компонент Picture для отображения списка со всеми изображениям и избранными. Такое неоднократное использование кода является одним из преимуществ React.

Результат работы нашего примера вы можете посмотреть на странице Работа с jQuery и AJAX вReact JS.

Создание сложной композитной формы с помощью React

Передо мной встала задача реализовать SPA со сложной формой с использованием React-Redux. Поскольку я ни разу не фронтендер, я начал читать доки, сделал какой-никакой базовый шаблон для приложения и дошел до, собственно, реализации формы.

Схема работы приложения следующая: компонент верхнего уровня загружает большой json-объект с сервера в память. Большой — это значит объект с большим количеством полей. Условно, все поля разделены на секции.

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

Сами поля и их стили предпочтительно генерировать самому, так как есть корпративный стиль и местные уже написанные контролы.

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

Для отображения и валидации полей возможны запросы на сервер (например, для данных для дропдаунов или для проверки валидности секции\поля).

Схематично это выглядит так:

По большому счету, вопроса возникает 2:

1) Какой предпочтительный метод для управления состоянием формы и модели данных?

С этим, в принципе, есть проблемы, но решаеммые. Насколько я понял, у меня 2 опции — хранить состояние в хранилище (сторе — я использую Redux), или хранить кусочки состояния в компонетнах и при каждой попытке закончить редактирование формы собирать это состояние и уже после обновлять хранилище.

Из того, что я понял в интернете и мауалов, делать ДЕЙСТВИЯ для обновления СОСТОЯНИЯ на каждый чих мне не стоит, так как контролов, доступных для редактирования много, а также в хранилище мне надо бы закидывать обновленную модель, которая уже прошла валидацию и сохранилась на сервере.

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

2) Какой предпочтительный способ генерации всех этих контролов на форме, добавления туда бизнес правил и правил валидации?

С этим ещё сложнее. Я смотрел рекомендованный вариант, redux-form, react-form, ещё раз react-form, react-redux-form, не особо популярные варианты и много что ещё и так и не смог не то, чтобы выбрать, но и понять общепринятый подход к построению сложных каскадных форм. Каждая вещь на примере 2-3 полей кажется приемлемой, но когда предсталяю моё количество полей, то понимаю, что я утону в эвент хандлерах и магических функциях. Отсюда вопрос — есть ли общепринятый, обкатанный способ реализации подобных нужной мне форм?

React Создание компонентов

пример

Это расширение базового примера:

Базовая структура

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

В этом случае некоторые люди считают предпочтительным использовать функциональные компоненты Stateless, которые основаны на функциях стрелок ES6 .

Функциональные компоненты без учета состояния

Во многих приложениях есть интеллектуальные компоненты, которые содержат состояние, но отображают немые компоненты, которые просто получают реквизит и возвращают HTML как JSX. Функциональные компоненты без учета состояния намного более многоразовые и оказывают положительное влияние на ваше приложение.

Они имеют две основные характеристики:

  1. При визуализации они получают объект со всеми реквизитами, которые были переданы
  2. Они должны вернуть JSX для отображения

Компоненты состояния

В отличие от вышеперечисленных компонентов «без состояния» компоненты «stateful» имеют объект состояния, который может быть обновлен с setState метода setState . Состояние должно быть инициализировано в constructor до его установки:

Расширение компонента с помощью PureComponent вместо Component будет автоматически реализовывать метод жизненного цикла shouldComponentUpdate() с неглубокой поддержкой и сравнением состояний. Это позволяет повысить эффективность вашего приложения за счет уменьшения количества ненужных визуализаций, которые происходят. Это предполагает, что ваши компоненты являются «чистыми» и всегда отображают один и тот же результат с одним и тем же состоянием и реквизитом.

Компоненты более высокого порядка

Компоненты более высокого порядка (HOC) позволяют совместно использовать функциональность компонента.

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

Создание компонента

В прошлом разделе, я упомянул, что ReactDOM.render принимает react-компонент (далее буду называть просто «компонент») и DOM-элемент, в который мы хотим «примонтировать» наше приложение.

h1 > Hello, world! h1 > — как ни странно, это примитивный компонент.

Пока ничего интересного, но давайте представим такой псевдо-код:

Что примечательного в псевдо-коде? Он очень хорошо читается, ведь очевидно, что наше приложение (App) отображает: фото (кошка, собака, сова), новости (какие-нибудь) и комментарии (тоже какие-нибудь).

Хочу вас обрадовать. React.js код выглядит практически так же. Он отлично читается, так как деление на компоненты позволяет отлично структурировать код.

Давайте создадим примитивный компонент.

Посмотрите в браузер, вы увидите разметку, которую мы указали в методе Render, нашего компонента App.

Конечно, кое-что добавил уже сам реакт, например data-react , это нормально.

Слово class в javascript является зарезервированным, поэтому внутри JSX синтаксиса необходимо писать className.

В ReactDOM.render мы передали целиком наш компонент, без свойств.

Запись App /> эквивалентна App > App > .

Итого: у нас есть компонент App />

Давайте разовьем идею, и добавим что-нибудь внутрь App, и отобразим это как ни в чем не бывало.

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

Во-первых — мы никак не изменили код внутри ReactDOM.render. Компонент App /> , содержит сейчас в себе другой компонент. Но при этом, это никак не влияет на «рендер» всего нашего приложения.

Во-вторых, как уже было сказано — компонент App /> содержит в себе компонент News /> . Да-да! Так же, как если бы это был просто дочерний div > div > элемент.

В-третьих, наш компонент News /> такой же примитивный, как и App, и содержит всего один (обязательный!) метод render.

Задачка на понимание происходящего: создайте компонент Comments /> и сделайте, чтобы он отображался после новостей. Текст компонента: «Нет новостей — комментировать нечего.»

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

Решение:

Откройте браузер, посмотрите разметку страницы. Все как мы написали + data-react атрибуты.

Ощущается некоторое неудобство, при использовании стандартного способа просмотра в консоли. Предлагаю вам установить react devtools (плагин для хрома, плагин для мозилы).

После установки, откройте вкладку React в консоли разработчика.

Пытливый читатель уже заметил окошечко «Props». Ок, об этом и поговорим в следующей главе.

Как создать в React формы: работа с компонентами, примеры кода

Учебный курс по React, часть 9: свойства компонентов

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

Занятие 19. Свойства компонентов в React

Создадим новый проект средствами create-react-app и изменим код нескольких стандартных файлов из папки src .

Вот код файла index.js :

Вот стили, которые описаны в файле index.css :

Вот код, находящийся в файле App.js :

Вот как будет выглядеть это приложение в браузере.

Страница приложения в браузере

Проанализировав код и внешний вид приложения можно прийти к выводу о том, что для вывода карточек со сведениями о животных хорошо было бы использовать особые компоненты. Сейчас эти элементы формируются средствами компонента App . Учитывая же то, о чём мы говорили на предыдущих занятиях, можно пойти и дальше — подумать об универсальном компоненте, который можно настраивать, передавая ему атрибуты или свойства.

В нашем приложении имеются карточки с изображениями кошек, их именами и контактными сведениями их владельцев (а может — и их самих) — телефоном и адресом электронной почты. Для того чтобы создать компонент, который в дальнейшем станет основой для всех подобных карточек, можно взять один из фрагментов разметки, возвращаемой компонентом App . Например — такой:

App возвращает четыре подобных блока, каждый из них можно было бы использовать для создания самостоятельного компонента, но такой подход нас не устраивает. Поэтому создадим один компонент, который станет основой всех карточек, выводимых приложением. Для этого создадим в папке src новый файл компонента — ContactCard.js и поместим в него код, который возвращает первый элемент

Ясно, что если создать несколько экземпляров этого компонента, то все они будут содержать одни и те же данные, так как эти данные жёстко заданы в коде компонента. А нам хотелось бы, чтобы, при создании разных экземпляров этого компонента, можно было бы настраивать выводимые им данные. Речь идёт о том, чтобы компоненту можно было бы передавать некие свойства, которыми он потом сможет воспользоваться.

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

Её можно использовать для того, чтобы узнать сумму чисел 1 и 1, но, например, для того, чтобы сложить 1 и 2, используя функции, которые не принимают никаких входных данных, нам пришлось бы писать новую функцию. Совершенно очевидно то, что такой подход приведёт к огромным неудобствам при необходимости сложения разных чисел, поэтому в подобной ситуации будет разумным создать универсальную функцию для сложения чисел, которая принимает два числа и возвращает их сумму:

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

Импортируем в файл App.js компонент ContactCard и вернём четыре его экземпляра, не удаляя пока код, который формирует карточки на странице приложения:

Теперь поработаем над кодом, используемым для создания экземпляров компонента ContactCard . Создавая обычные HTML-элементы, мы можем настраивать их атрибуты, влияющие на их поведение и внешний вид. Имена этих атрибутов жёстко заданы стандартом. В случае с компонентами можно воспользоваться точно таким же подходом, с той только разницей, что имена атрибутов мы придумываем сами, и сами же решаем — как именно они будут использованы в коде компонента.

Каждая из карточек содержит четыре фрагмента информации, которые, от карточки к карточке, могут меняться. Это — изображение кошки и её имя, а также телефон и адрес электронной почты. Пусть имя кошки будет содержаться в свойстве name , адрес изображения — в свойстве imgURL , телефон — в свойстве phone , а адрес электронной почты — в свойстве email .

Зададим эти свойства экземплярам компонентов ContactCard и, по мере переноса данных из кода, который уже имеется в App , будем удалять соответствующие его фрагменты. В результате код компонента App будет выглядеть так:

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

Данные карточек жёстко заданы в коде, компонент не умеет работать с переданными ему свойствами

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

Приступим к решению этой задачи, указав, при объявлении функции ContactCard , что она принимает параметр props . При этом код компонента будет выглядеть так:

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

Параметр props — это объект. Свойствами этого объекта являются свойства, переданные компоненту при создании его экземпляра. То есть, например, в нашем объекте props будет свойство props.name , содержащее имя кошки, переданное компоненту при создании его экземпляра. Кроме того, у него будут свойства props.imgUrl , props.phone , props.email . Для того чтобы в этом убедиться, добавим в начало функции ContactCard команду console.log(props) .

Это позволит вывести объект props , получаемый компонентом, в консоль.

Объект props в консоли

Тут можно видеть вывод четырёх объектов из ContactCard.js . Их именно столько из-за того, что мы создаём четыре экземпляра компонента ContactCard .

Всё это даёт нам возможность использовать в коде компонента, вместо жёстко заданных значений, то, что передано ему при создании его экземпляра, доступное в виде свойств объекта props .

Что если мы попытаемся воспользоваться свойством props.imgUrl так:

На первый взгляд такая конструкция может сработать, но вспомним о том, что тут нам нужно использовать сущность из JavaScript в JSX-коде. О том, как это делается, мы говорили на одном из предыдущих занятий. А именно, в нашем случае свойство объекта нужно заключить в фигурные скобки:

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

Обратите внимание на то, что в полях для вывода телефона и адреса электронной почты мы оставили тексты Phone: и Email: с пробелами, следующими за ними, так как эти тексты используются во всех компонентах. Если теперь взглянуть на страницу приложения, то можно заметить, что она содержит четыре разных карточки.

Страница, сформированная с использованием универсального компонента

Наш компонент принимает всего четыре свойства. Что если некоему компоненту нужно будет, например, передать 50 свойств? Пожалуй, передавать каждое такое свойство отдельной строкой, как это сделано в компоненте App , будет неудобно. В таких случаях можно воспользоваться другим способом передачи свойств компонентам. Он заключается в том, что, при создании экземпляра компонента, ему передаётся не список свойств, а объект со свойствами. Вот как это может выглядеть на примере первого компонента:


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

В ходе модификации кода компонента App , используемого для создания первого экземпляра компонента ContactCard , правильная работа приложения была нарушена. Вот как теперь будет выглядеть его страница.

Нарушение правильной работы приложения

Как это можно это исправить? Для того чтобы в этом разобраться, полезно будет проанализировать происходящее с помощью команды console.log(props) .

Анализ объекта props

Как видно, объект props первого компонента отличается от такого же объекта второго и следующих компонентов.

В компоненте ContactCard мы пользуемся объектом props исходя из предположения о том, что у него есть свойства name , imgUrl и прочие подобные. Здесь же первый компонент получает лишь одно свойство — contact . Это приводит к тому, что у объекта props оказывается лишь одно свойство — contact , являющееся объектом, а в коде компонента работа с подобной структурой не предусмотрена.

Перевести наш компонент на модель использования лишь одного свойства-объекта contact , содержащего другие свойства, довольно просто. Для этого, например, для доступа к свойству name , достаточно воспользоваться конструкцией вида props.contact.name в коде компонента. Аналогичные конструкции позволяют правильно работать с другими нужными нам свойствами.

Переработаем код компонента с учётом передачи ему единственного свойства-объекта contact , содержащего другие свойства:

Первый компонент теперь должен будет выводиться нормально, но мы этого, на данном этапе работы над проектом, не увидим, так как система сообщит нам о множестве ошибок, связанных с тем, что несколько экземпляров компонента ContactCard , создаваемые в компоненте App , не получают свойство-объект contact . При выполнении кода это свойство будет иметь значение undefined . В результате производится попытка обратиться к некоему свойству значения undefined , что и приводит к возникновению ошибки. Исправим это, переработав код компонента App , ответственный за формирование компонентов ContactCard :

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

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

Итоги

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

14 февраля 2020 г. 17:11

Если вам понравилась эта статья поделитесь ею с друзьями, тем самым вы помогаете нам развиваться и добавлять всё больше интересного и полезного контента!

# 5 React — как работать с формами

Всем привет. В этом уроке мы разберем как работать в реакте с формами.

Создадим компонент с формой регистрации. Создадим файл RegistrationForm.js

Добавим туда пустую заготовку под компонент

И импортируем ее в App.js и уберем ненужный код с прошлого урока.

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

Давайте теперь добавим форму и submit метод.

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

Создадим конструктор, вызываем super и задаем дефолтное значение для email.

Добавим input в нашу форму.

Как значение зададим this.state.email. Также нам нужно задать onChange функцию. Она будет стрелять каждый раз, когда мы изменяем email.

И добавим метод handleSubmit и handleEmailChange

Если мы законсолим this в функции handleEmailChange, то увидим в браузере, что он undefined, так же, как у нас было в уроке про локальный стейт. В тот раз мы писали bind прямо в методе .bind(this). Другой вариант это написать bind в конструкторе.

Это просто переприсваивает функции с правильным this.

Теперь в браузере мы видим правильный this.

Давайте теперь когда стреляет функция handleEmailChange сетить значение в стейт.

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

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

Как вы видите, у нас сразу же в инпуте отображаются данные. Если мы уберем строчку setState, то мы будем что-то печатать и у нас ничего не отображается, поскольку значение this.state.email не изменилось. Как только мы вызываем setState, у нас отображается значение с локального стейта.

Давайте изменим немного сообщение в handleSubmit.

Как мы видим в браузере, если мы напишем что-то в input и нажмем enter, console.log у нас выводится, но страница перегружается так как это обычная форма. Для того, чтобы это пофиксить давайте добавим event.preventDefault().

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

Ну и напоследок давайте добавим кнопку для сабмита формы

Так как у button, по умолчанию тип submit, и наша форма отправляется и мы попадаем в функцию handleSubmit.

Siarhei Dudko iT blog

Здесь я буду писать всякие интересные штуки из моей жизни и работы

Создание независимого компонента для React и его публикация в npm.

Предисловие

Началось все с того, что нужно было разработать новый интерфейс для MS-SQL отчетов. Разработать его нужно было в уже готовом проекте, который достался «по наследству». Сам проект написан на ajax. Коллега очень просил сделать интерфейс «как в Delphi», собственно так и назовем наш проект. Для реализации формы я выбрал react, т.к. на ajax писать скучно и геморройно, а react на данный момент я освоил вполне хорошо. Чтобы не было проблемы с совместимостью, интерфейс буду подгружать как фрейм. В предыдущих проектах я использовал umd пакеты react и тут возник вопрос, нужно было подключить календарь. Самому писать его было лень — нашел пару готовых, ни один из них в таком режиме не заводился. В связи с этим пришлось озадачиться с упаковкой проекта webpack-ом. И тут было куча мануалов, которые ни к чему не приводили, т.к. на актуальную 4-ю версию вебпака их просто не было. Официальный мануал тоже довольно запутан. Чуть ниже я опишу минимальный конфиг. Сам интерфейс построен на той же логике, но немного в иной реализации. (Исходники фронтэнда можно посмотреть тут: https://github.com/siarheidudko/farmin-mssqlreportfrontend , а реализацию, с отключенным бэкэндом, тут: https://test.sergdudko.tk/mssql/form1/ ). Логика обработки данных в интерфейсе такова:

  1. Непосредственная логика полностью управляется в диспатчере редакса.
  2. Изначально в state загружается клон данных редакса.
  3. В визуализации компонента в качестве свойств элементов DOM можно ссылаться прямо на редакс только в том случае, если эти свойства const. Иначе это должен быть либо клон редакса, либо state компонента.
  4. При изменении мы выкидываем новое свойство элемента DOM в обработчик, который обновляет данные в редаксе.
  5. При монтировании компонента мы подписываем state компонента на соответствующие данные в редаксе с обязательной проверкой, что они не эквивалентны. Если проверку опустить, даже PureComponent рендерится чаще чем нужно.
  6. Возвращаемся к пункту 3.

В примере я запихнул всю логику в state компонента. Но сам принцип связи DOM элементов не изменился.

Webpack

Для начала установим зависимости:

Предполагаю, что глобально у вас уже стоит webpack (у меня был версии 4.8.3) и webpack-cli.

Итак, минимальный конфиг для упаковки react приложения. Создадим в корне файл .babelrc:

Если, конечно, вы не пишете на ES5/ES6 синтаксисе — можете обойтись и без него. Далее нам нужен корректный package.json, рассмотрим на примере моего, разбирать по полям все не стану — только основное:

  • name — наименование пакета в npm
  • version — версия пакета в npm
  • main — путь к первому скрипту пакета
  • dependencies — зависимости, которые потянет пакет. Вебпак у меня не обязательный, просто без него не упакуется.
  • scripts — скрипты запускаемые командой npm run имя_скрипта

Ну и сам конфиг webpack.config.js:

Если не указать entry — он будет искать скрипт, указанный в main package.json, что в намем случае не вариант — в пакете будет уже скомпилированный umd (см. libraryTarget) пакет.

Компонент

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

Соответственно нам теперь нужно создать два скрипта DelphiForm1.js и DelphiForm2.js в которых описать компоненты доступные по ссылкам form1 и form2 нашего модуля.

В DelphiForm2.js я запишу просто тестовую форму, чтобы она выводила хоть что-то (слово TEST).

А DelphiForm1.js будет наш компонент формы. Визуально он будет представлять из себя:

Логика работы компонента:

Мы получаем данные в props. Допустимые данные

  • store — объект json, где ключами являются уникальные идентификаторы (далее uid), а значением наименование.
  • selected — массив uid-ов, которые будут изначально выбраны (нужно для реализации сохранения состояния компонента).
  • name — имя компонента.
  • callback — функция обратного вызова, в которую будет возвращен массив selected при инициализации компонента и далее будет возвращаться массив выбранных элементов из правой формы.

React на практике: приложение с сортировкой и поиском данных

В первом выпуске React Challenge я предложил вам построить небольшое приложение на React, чтобы вы смогли прощупать библиотеку на более сложном проекте, чем, например, TODOList. Для выполнения челенджа у вас был небольшой стартовый шаблон, который отвечал за компилирование JavaScript и Sass, а также за генерирование случайных данных для приложения. Всего в первом челендже по официальным данным (количество форков на Github) приняло участие более 150 человек и многие даже довели дело до конца. Настало время во всём разобраться и подвести итоги.

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

Загружаем данные

Для загрузки данных напишем для себя отдельный модуль, который назовём load.js и положим в папку utils . Загрузка данных будет происходить асинхронно после инициализации самого приложения, поэтому удобно воспользоваться промисами:

Теперь мы можем асинхронно загрузить любой файл с сервера подобным образом

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

Главный компонент App

Состояние

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

Загружаем данные

Для загрузки данных проще всего создать отдельный метод. Назовём его условно loadData . Используя компонент, мы можем указать его свойства, передавая “атрибуты”. В данном случае будет очень удобно указать подобный параметр для компонента для обозначения того, какой файл необходимо загрузить.

Общаемся с другими компонентами

Чтобы дать возможность дочерним компонентам обновлять состояние, создадим ещё один метод, который назовём updateData . Метод будет принимать объект и просто устанавливать его в качестве текущего состояния:

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

Всё вместе

Мы хорошо подготовились к созданию других компонентов, но до сих пор не написали содержимое метода render . Но приступим к этому только в самом конце, когда будут готовы все остальные компоненты. Пока же ограничимся обычным Hello, World! :

Отображение

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

Отображение всех данных (компонент UserList)

Очевидно, что для простого отображения данных нет необходимости создавать компоненты с состоянием. Поэтому для такой простой операции воспользуемся функциональным компонентом (functional stateless component).

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

Получается ряд таблицы со всеми нужными нам данными. Что-то напоминает? Вы угадали, это ещё один компонент! Назовём его UserData .

И сразу возникает много вопросов:

Отлично! Теперь у нас есть заготовка для шаблонизации и мы смело можем переходить к построению всего списка в .

Магия шаблонизации

Мы уже выяснили, что для компонента нет необходимости в состоянии, поэтому, как и в примере выше, мы можем обойтись функциональным вариантом. Компонент представляет из себя таблицу с четырьмя столбцами: изображением, именем, возрастом и телефоном. На ряду с “динамическими” данными, загруженными с сервера, компонент содержит статическую часть — заголовки для таблиц. Поэтому сразу можем наметить для себя базовую разметку:

Данные есть, компонент для шаблонизации данных есть, осталось только придумать способ, как все из наших данных (обычных объектов) сделать компонент. Всё просто. Пройдёмся по массиву данных с помощью map и создадим новый массив компонентов.

У нас есть массив компонентов , и что дальше? Помним, что React чрезвычайно умный и знает, как решить нашу проблему. Чтобы вывести все данные, всё, что нам необходимо сделать это показать, где они необходимы.

И это всё, что от нас требуется. Почти. Остаётся только подумать о том, что будет происходить, если мы не получили данные. В текущем состоянии мы получим ошибку, data будет содержать null и, соответственно, не получится использовать map . Чтобы не попасть в такую неприятную ситуацию, первое, что нам необходимо сделать — проверить, были ли получены данные.

Теперь все данные отображаются на странице и работает магическая шаблонизация. Весь код компонента :

Текущий пользователь (компонент ActiveUser)

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

Всё готово! Теперь можно обновить метод render главного компонента и увидеть результат:

Ищем пользователей

Искать пользователей будем по имени. Для реализации поиска создадим компонент . Для этого в компоненте создадим поле ввода, которое будем отслеживать на событие change. При вводе или удалении текста событие будет незамедлительно срабатывать и отсеивать или добавлять результаты в поиск. Компонент опять будет написан в функциональном стиле.

Базовая разметка для компонента готова. Также мы создали функцию dataSearch , которая и будет творить чудеса. На входе компонент принимает три параметра: term — строка, показывающая приложению, что мы ищем в данный момент, data — исходные данные, которые будем фильтровать и update — функция для обновления состояния компонента . Приступим к фильтрации:

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

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

Теперь мы всегда сможем получить доступ к исходным данным внутри любого компонента нашего приложения. Весь код для компонента :

Отобразим поиск на странице:

Сортируем данные (компонент Toolbar)

В тулбаре присутствует три кнопки: сортировка по имени, по возрасту и третья, бонусная (изначально не предполагалась) кнопка для очистки поля ввода и удаления всех сортировок. Первые две кнопки запускают сортировку данных с помощью метода sort в зависимости от переданного им параметра ( name или age ). Также для определения того, в каком порядке стоит сортировать данные, мы создали свойство sorted с объектом, указывающим на очерёдность. Теперь мы полностью готовы написать метод sort :

Методом sort будут пользоваться первые две кнопки тулбара, для третьей кнопки необходимо написать ещё один метод reset , который восстановит изначальное состояние приложения:

Мы почти закончили. Осталось добавить компонент в :

Итоги

Написанное мной решение, которое я попробовал вам подробно описать не является единственным или же лучшим. В течении прошедших трёх недель в рамках первого выпуска React Challenge данную задачу решили ещё несколько десятков человек. Всё, что у них получилось, вы сможете найти здесь. Демо конечного приложения можно посмотреть здесь, весь исходный код, написанный в данной статье можно найти в этом репозитории. Спасибо за участие! Буду жать вас на следующем челендже!

Новый уровень React: Компоненты-контейнеры

Серия статей
Часть 1: React Router
Часть 2: Компоненты-контейнеры (вы здесь!)
Часть 3: Redux

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

Также мы поговорим о данных в приложении. Если вы знакомы с каким-либо component-based шаблоном или MVC, вы, вероятно, знаете, что смешивать представление и логику в приложении — это, как правило, плохая практика. Другими словами, представление должно получать данные и отображать их, оно не должно знать, откуда они приходят, как изменяются или как создаются.

Получение данных с помощью Ajax

В качестве примера плохой практики давайте добавим в компонент UserList из предыдущего урока получение данных с сервера:

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

Поясню, нет ничего плохого в том, чтобы использовать getInitialState для инициализации начального состояния компонента, и нет ничего плохого в том, чтобы сделать ajax-запрос в методе componentDidMount (хотя, вероятно, следует абстрагироваться от вызова конкретных функций). Проблема в том, что мы делаем эти вещи в том же компоненте, который отвечает за отображение данных. Такое сильное связывание уменьшает возможность изменения и повторного использования. Что, если вам нужно получить список пользователей где-то еще? Получение списка пользователей привязано к данному представлению и не может использоваться повторно.

Вторая проблема заключается в использовании jQuery для отправки ajax-запроса. Уверен, jQuery делает много других полезных вещей, но большинство из них связано с манипуляциями с DOM, а в React для этого используется собственный подход. Для особенностей jQuery, не связанных с DOM, можно найти множество более подходящих альтернатив.

Одна из таких альтернатив — Axios, инструмент для отправки ajax-запросов, основанный на промисах, очень похожий на jQuery. Правда похоже?

Свойства и состояние

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

Свойства и состояние связаны в том смысле, что они содержат своего рода «модель» для компонентов React. Они могут передаваться от родительских компонентов к дочерним.

В качестве примера, скажем, ComponentA передает свои своства и состояние дочернему компоненту ComponentB. Метод render компонента ComponentA выглядит следующим образом:

Несмотря на то, что foo — состояние родительского компонента, в дочернем оно доступно как свойство. Атрибут bar также доступен как свойство в компоненте ComponentB, поскольку все данные, переданные в дочерний компонент, доступны в нем как свойства. Пример ниже показывает, как компонент ComponentB может получить доступ к свойствам foo и bar:

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

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

Пришло время разделения

В разделе Получение данных с Ajax мы создали проблему. Компонент UserList работает, но он пытается делать слишком много вещей. Чтобы решить эту проблему, разделим UserList на два компонента, каждый из которых будет выполнять одну свою функцию. Эти два типа компонентов будем называть компоненты-контейнеры и компоненты-представления, или «умные» и «глупые» компоненты.

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

Компоненты-представления

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

Это не совсем тот компонент, который мы использовали ранее, но это как раз и есть компонент-представление. Разница между ним и оригиналом в том, что этот компонент выводит список элементов цикле, а данные получает в качестве свойства.

Компоненты-представления являются «глупыми» в том смысле, что они не представляют, откуда берутся данные. Они ничего не знают о состоянии.

Компоненты-представления не должны изменять данные. Фактически, любой компонент, получающий свойства от родителя, должен держать их неизменяемыми. В то же время, они могут как-либо форматировать данные (например, конвертируя Unix timestamp в читаемую дату-время).

В React обработчики событий связываются непосредственно в представлении с помощью атрибутов, таких как onClick. Возникает вопрос, как работают обработчики событий в компонентах-представлениях, которые не могут изменять свойства. Об этом у нас есть целый раздел ниже.

Циклы

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

Если вложенный return вас пугает, создание элемента выделить в отдельную функцию:

Компоненты-контейнеры

Компоненты-контейнеры чаще всего являются родителями для компонентов-представлений и обеспечивают связь между представлениями и остальными частями приложения. Их также называют «умными» компонентами, поскольку они знают о приложении в целом.

Компонент-контейнер и компонент-представление должны иметь разные имена, чтобы избежать путаницы, назовем контейнер UserListContainer:

Компонент-контейнер создается как любой другой компонент React. Он имеет метод render, но не создает элементов DOM, а только возвращает компонент-представление.

События

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

Чтобы понять проблему, добавим событие в наш компонент-представление (клик по элементу ):

Технически это будет работать, но это плохая идея.

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

Лучшим решением будет передать функцию из контейнера в представление в качестве свойства:

Атрибут onClick задан в представлении, а функция вынесена в контейнер. Это более подходящее решение, поскольку контейнер отвечает за состояние.

В родительском компоненте происходит изменение состояния, при этом вызывается метод render, который обновляет свойства дочернего компонента, что, в свою очередь, приводит в перерисовке дочернего компонента. Это происходит в React автоматически.

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

Использование компонентов-контейнеров с роутером

Теперь маршрутизатору нет необходимости использовать напрямую UserList. Вместо этого он будет использовать UserListContainer, который возвращает UserList. И в конечном итоге маршрутизатор получает то, что ему нужно.

Поток данных и оператор расширения

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

В ES6 добавился новый очень полезный оператор расширения. React позаимствовал этот синтаксис для JSX, он упрощает поток данных через свойства. В примерах на GitHub используется этот синтаксис, не забудьте также прочитать объяснение.

Функциональные компоненты без состояния

В версии 0.14 (выпущенной в конце 2015 года) появилась новая особенность — функциональные компоненты без состояния (Stateless Functional Components).

Теперь, когда мы разделили компоненты-контейнеры и компоненты-представления, вы наверняка заметили, что большинство компонентов-представлений состоят из одного метода render. В этом случае React позволяет записать компонент в виде одной функции:

Вы можете видеть, что новый способ является более компактным. Но помните, что этот способ подходит только для компонентов, состоящих из одного метода render.

Stateless-компонент принимает в качестве аргумента объект props. В этом случае не нужно использовать this для доступа к свойствам.

На Egghead.io есть отличное видео, посвященное stateless-компонентам.

Как вы могли заметить, React — не традиционный MVC-фреймворк. Часто говорят, что React выполняет только роль представления. Это не совсем верное утверждение и может ввести в заблуждение начинающего. Может показаться, что React необходимо использовать вместе со сторонним MVC-фреймворком.

Хотя это правда, что в React нет контроллеров, в традиционном понимании, но он предоставляет другой способ разделения представления и поведения. Компоненты-контейнеры выполняют роль контроллеров в традиционной модели MVC.

Что касается моделей, я видел, как люди используют модели из Backbone вместе с React, и я уверен, что у них есть все основания считать, что это хорошо. Но я не уверен, что традиционные модели подходят для React. Поток данных в React не работает хорошо с традиционными моделями. Шаблон Flux, разработанный в Facebook, описывает поток данных в React. В следующем уроке мы поговорим о Redux, популярной реализации Flux, которую можно рассматривать как альтернативу традиционным моделям.

Заключение

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

Данный урок был написан под влиянием других статей по этой теме. Обязательно посмотрите официальное руководство на GitHub, чтобы получить больше информации и примеров компонентов-контейнеров.

Нашли опечатку? Orphus: Ctrl+Enter

© getinstance.info Все права защищены. 2014–2020

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

Топ-пост этого месяца:  Добавить слово и фразу в заголовок и в тело контента всех записей
Добавить комментарий