Все фундаментальные принципы реакта описаны на его главном экране
Реакт – имеет декларативный (что?) подход, то есть в нём мы описываем, какой результат нам нужен
Императивный (как?) полностью описывает как дойти до определённого результата – описаны конкретные шаги
Реакт основан на компонентах (элементы которые могут повторяться, но иметь разное содержимое)
На реакте построено множество других библиотек, которыми можно писать те же мобильные приложения или приложения для VR
Отличительные особенности React
Использует препроцессор JSX, который позволяет писать JS и вёрстку в одном месте
В нём есть механизмы сравнения, которые позволяют отследить только те участки веб-приложения, которые изменились и изменить только их, а не всё приложение полностью (reconciliation algorithm)
Виртуальное дерево. Реакт работает сначала с виртуальным деревом, которое он создаёт для себя. Потом уже изменения переносятся на основное дерево.
Оптимизация. Она вытекает из прошлого пункта и обеспечивается более компактным формированием информации в объектах виртуального ДОМ Реакта
Пример обычного компонента в ДОМ-дереве: огромное количество свойств и строк
И пример ДОМ-элемента в реакте. Он имеет только небольшое количество свойств, что позволяет снять нагрузку на память
004 Create React App - создаем свое приложение
Чтобы создать реакт-проект, можно воспользоваться утилитой create-react-app, которая загрузит сразу шаблон
Это два основных файла в src. Всё остальное - это стили и тесты.
Это index.html, в котором и будет рендериться наша страница
005 Работаем с JSX-препроцессором, ограничения в нем
При установке реакт-проекта, мы начинаем работать с index.js
И вот так вот выглядит препроцессор JSX в написании: мы пишем HTML прямо внутри JS и выводим его через рендер. Рендер должен быть только один!
А это старый вариант записи, который работает под капотом у препроцессора JSX
После выполнения метода выше, нам возвращается вот такой вот объект:
Так же нужно сказать, что в каждый файл, где используется препроцессор JSX, не нужно импортировать React - достаточно иметь импорт в основном файле
И нужно описать несколько правил написания такого многострочного HTML внутри JSX:
Все элементы нужно обернуть внутрь одного родителя <div><div/>
Теги можем записать как самозакрывающиеся - <button />, так и полные - <button></button>
Если не обернём элементы внутрь одного тега, то получим такую ошибку
Ну и так же JSX позволяет вставлять интерполяцию { }, в которую можно вставлять переменные и выполнять функции
Единственное исключение - в эту интерполяцию нельзя вкладывать объекты. На выходе мы получим ошибку, так как тут идёт конвертация объекта в строку - Object object. Делается конвертация для обеспечения безопасности сайта (чтобы не было вмешательств со стороны)
Все атрибуты тегов записываются в camelCase и имеется два атрибута, которые имеют отличное написание от того, что есть в оригинальном HTML - это className = class и htmlFor = for
006 Элементы и компоненты
Компонент - это блок пользовательского интерфейса, который может иметь своё поведение.
Это самостоятельная структура, которую мы можем переиспользовать в различных местах приложения.
В первую очередь, компонент представляет из себя функцию, которая возвращает JSX блок кода.
Элемент - это структурная часть компонента.
И тут мы столкнёмся с другой особенностью этой библиотеки - мы не можем изменить уже отрендеренный элемент на странице. Для этого нужно поменять его состояние.
Компонент записывается в виде функции и используется при рендере в виде тега.
И вот пример использования компонентов в React. Компоненты в блоке рендера на странице записываются внутри:
<Компонент/> или <Компонент> Вложение </Компонент>
Имя компонента всегда обязательно должно начинаться с заглавной буквы, а иначе babel воспримет и скомпилирует его как элемент!
Тут мы вызваем рендер основного компонента App из корневого файла index.js.
Вложить в компонент можно любой элемент и так же можно запустить функцию
Так же можно использовать любые выражения внутри {}. Конкретно тут, если пользователь залогинен, то он может выйти. Если не залогинен, то может войти. Кнопка вход/выход.
Так же в атрибуты мы можем помещать переменные.
Например, стили мы можем записать отдельно в виде объекта и передавать их внутрь компонента через переменную.
Так же мы можем записать компоненты в виде классов.
Первый вариант:
Более короткий вариант через деструктуризацию:
В каждом классе компонента должен быть обязательный метод render(), который будет отрисовывать интерфейс.
Если мы используем функциональные компоненты, то они просто должны иметь директиву return, которая вернёт JSX.Element
007 Строгий режим
Строгий режим внутри реакта подразумевает под собой проверку на актуальные конструкции, которые мы можем использовать. Обычно он используется для перевода проекта на новую версию реакта.
Если какой-то компонент или подход не будет соответствовать нынешней версии реакта или будет нерекоммендуемым к использованию, то реакт нас об этом предупредит.
Строгий режим работает только на dev-проекте. На выпущенном в продакшн стрикт уже не сработает, так как подразумевается, что мы всё отработали.
Так же этот режим можно использовать в отдельных компонентах, чтобы проверять только их
009 Создание нового проекта
CRUD — акроним, обозначающий четыре базовые функции, используемые при работе с базами данных: создание (create), чтение (read), модификация (update), удаление (delete). Это стандартная классификация функций по манипуляции данными.
В SQL этим функциям, операциям соответствуют операторы Insert (создание записей), Select (чтение записей), Update (редактирование записей), Delete (удаление записей).
Конкретно будет реализовываться проект, который позволит добавлять, удалять, изменять и читать сотрудников и как-то с этими данными взаимодействовать
И в самом начале нам нужно поделить макет на отдельные составные части - компоненты, которые будут представлять собой интерфейс приложения
Первым делом, нужно добавить определённые библиотеки в HTML для стилизации приложения: bootstrap и font-awesome
Для начала определимся со структурой. В папке src храним все исходники проекта. Внутри этой папки нужно хранить два основных файла index js и css. Там же создадим папку components, которая будет хранить все реакт-файлы. Все компоненты делим по папкам, которые именуем в порядке cebab-case. Основной папкой тут будет App и его JS-файл
Сразу нужно сказать, что без разницы, будем мы писать JS или JSX расширение файла - вебпак всё соберёт
Основной файл рендера:
index.js
Основной файл для компонентов, который отправляется на рендер:
app.js
Пример компонента, который хранит в себе другой компонент:
employees-list.js
Стили компонента:
employees-list.css
Пример полноценного компонента со стилями:
employees-list-item.js
defaultValue - это атрибут инпута, который задаёт начальное значение по умолчанию
010 Свойства компонентов
Свойства компонентов (properties - пропсы) - это объект, который передаётся в компонент из вызова компонента на рендер. Сами значения пропса хранятся в атрибутах компонента.
Значения атрибутов неизменяемые - они только на чтение. Кроме как отобразить на странице и прочесть значение - больше ничего не получится.
Пропсы используются в компоненте. А менять пропсы в компонентах - нельзя.
app.js
Однако чаще используется деструктуризация пропса
Ну и так же подобный вариант передачи значения - в виде объекта. Мы можем положить в атрибут объект с определёнными свойствами и эти свойства вызвать в функции
Однако нужно быть с этим способом аккуратнее, так как если не указать свойство объекта, то мы получим ошибку
Так же при вызове компонента мы можем вызвать функцию и такой функционал у нас тоже отработает
011 Практика свойств на проекте
Нам нужно вывести разных пользователей
Первый вариант:
Второй вариант:
Тут мы уже передаём массив объектов в компонент в качестве пропса и внутри компонента срабатывает логика генерации массива дочерних компонентов, которые представляю из себя вёрстку
Модифицируем через spread-оператор:
Запись через {...item} равна name={item.name} salary={item.salary}
Если нам нужно модифицировать класс:
Тут уже нужно реализовать реагирование элемента на повышение зарплаты
012 Работа со списками и алгоритм согласования
Современные веб-приложения представляют из себя динамически-меняющиеся документы, которые представляются в вебе. То есть мы можем динамически менять элементы на странице в зависимости от действий пользователя.
При изменении структуры документа в реакте, начинает работать алгоритм реконцеляции или Reconciliation. Он представляет из себя полное перерендеривание элемента на странице, что сильно упрощает работу с изменением элементов и их обновлением.
Этот алгоритм работает рекурсивно и обновляет как сам элемент, так и все вложенные внутри него.
Реакт сохраняет прошлую версию дерева и новую версию дерева (всё находится внутри Virtual DOM). Производит сравнение внутри себя каждого элемента друг с другом и если находит отличия, то производит перерисовку элемента в реальном ДОМ-дереве. То есть перерисовка происходит ровно у тех элементов, которые изменились - остальные остаются нетронутыми.
Первый вариант:
Пример изменения, при котором произойдёт перерендер:
Однако из такого подхода вытекает следующий минус:
Если мы можем в конец этого же массива добавить новый элемент и реакт его просто дорисует, то если добавить новую строчку в начало массива, то реакт перерисует всё, так как порядок нумерации элементов в его дереве был сбит
Стоковый массив работников:
Без полного перерендера:
С полным перерендером:
И чтобы реакт не перерендеривал одинаковые элементы постоянно, нужно добавить в качестве атрибута тегу уникальный key
Добавляем в объект деструктуризацию и передаём не весь item, а все его свойства по отдельности и id (который поместим в атрибут key):
Либо подойдёт такой вариант:
И теперь в девтулзе мы можем увидеть атрибут key, который будет помечать для реакта повторяющийся компонент и не давать ему его перерендеривать, если тот не изменён
Важный момент: мы должны понимать, что порядок элементов у нас не поменяется, а не иначе смысла в этих атрибутах практического не будет
Особенности быстрой работы реакта:
Реакт обновляет только те элементы интерфейса, которые действительно изменились
В этом ему помогает алгоритм согласования, который сравнивает старую и новую копию ДОМ-дерева
При работе со списком одинаковых сущностей лучше использовать атрибут key, чтобы реакт не перерендеривал страницу и работал ещё быстрее
013 Состояния компонентов
Перепишем немного приложение из первых уроков под классы с использованием компонентов:
И конкретно в React мы можем прописать переменную, которая будет хранить в себе значения определённых состояний. Состояния - это динамически изменяемые объекты на странице. Мы не можем их изменять напрямую, но можем попробовать поменять особым образом
И в этом примере в метод рендера мы закинули кнопку, которая будет вызвать метод для смены состояния и написали сам метод для смены состояния.
Для смены состояний обязательно нужно использовать стрелочные функции, чтобы наследовать контекст.
Сам объект мы не мутируем - нужно передавать внутри setState() новый объект с нужным значением.
setState() - при запуске активирует перерисовку всего компонента с новым состоянием
Особенности работы хуков:
Функция setState асинхронна, поэтому когда мы её очень быстро выполняем, она может не успеть поменять состояние объекта и мы можем пропустить изменения.
В React есть механизмы для объединения сразу нескольких изменений состояний в одно изменение состояния.
Чтобы избежать проблем с асинхронным изменением состояния, нужно использовать для изменения объекта колбэк-функцию
Тут уже нужно отметить несколько моментов.
Сейчас в метод setState мы передаём коллбэк-функцию, которая заставит Реакт сначала выполнить текущее изменение состояния, а уже только потом изменять новое состояние.
Сам объект {}, который передаётся через коллбэк оборачивается в скобки, чтобы не вызвать внутри return, поэтому получается такая конструкция: state => ({})
setState меняет состояние только тех свойств объекта state, которые мы в него передали. То есть свойство text (которое мы отображаем в качестве текста кнопки) меняться не будет и перезагружаться на странице тоже, что снижает потребление ресурсов
Итог:
У компонента может быть своё внутреннее состояние, которое динамически меняется
Состояние может быть как у классовых, так и у функциональных компонентов
Состояние напрямую менять нельзя - только через команду setState
setState и какое-либо изменение состояния - это асинхронная операция и если мы хотим сохранить точность и последовательность данных, нужно передавать коллбек-функцию
В команде setState мы можем менять только те свойства объекта, которые нам нужны - остальные останутся без изменений
014 Самостоятельное задание на работу с состояниями
Задание:
Начальное значение счетчика должно передаваться через props
INC и DEC увеличивают и уменьшают счетчик соответственно на 1. Без ограничений, но можете добавить границу в -50/50. По достижению границы ничего не происходит
RND изменяет счетчик в случайное значение от -50 до 50. Конструкцию можете прогуглить за 20 секунд :) Не зависит от предыдущего состояния
RESET сбрасывает счетчик в 0 или в начальное значение из пропсов. Выберите один из вариантов
Начальный код:
Результат:
All is works!@!!
015 События в React и вспоминаем this
Сейчас нужно поговорить о том, почему мы используем коллбэк-функции вместо обычных, когда нам нужно работать в реакте.
Причина крайне проста: методы, которые мы вызываем в другом методе, при использовании обычных функций, не сохраняют контекст вызова. Коллбэк-функция nextYear сохраняет контекст вызова и обращается к классу, в котором находится метод render. Обычная функция не сохраняет контекст и this внутри неё будет = undefined
Решить проблему мы можем не только коллбэк-функцией, но и другими способами:
Первый способ: привязка контекста через bind
Второй способ: непосредственное использование классов с вложенными в них коллбэк-функциями
Третий способ: использование анонимной стрелочной функции. Тут мы вызываем функцию nextYear внутри стрелочной функции, которая позволяет сохранить контекст вызова.
Проблемным этот способ может быть, если мы соберёмся передавать такой вызов в качестве пропсов в другие вложенные компоненты
Если нам нужно передать в функцию значения, то мы должны воспользоваться анонимной стрелочной функцией (e) => this.commitInputChanges(e, 'red'), которая и позволит передать значения.
Если нам нужно задать триггер функции для тега input стоит пользоваться атрибутом onChange
Так же нужно отметить, что event передаётся в функцию по умолчанию и нам не нужно передавать его отдельно, поэтому для вызова функции для нас доступна такая запись: this.commitInputChanges
016 Практика состояний на проекте
Конкретно сейчас нам нужно реализовать добавление звёздочек на работников, которым назначена премия
Тут полетели стили, но всё работает именно так, как должно было
017 Работа с формами, управляемые и неуправляемые компоненты
Конкретно тут мы сделали запись значения из инпута в стейт кмопонента, тем самым сделав его управляемым.
Если метод связан с определёнными действиями пользователя, то наименование стоит начинать с on (например onValueChange - см. пример выше в коде)
Конкретно, если в атрибуте стоит value={определённое_значение}, то мы всегда будем иметь актуальный стейт. То есть значение value формы инпута будет контролироваться реактом. Сам такой элемент будет называться управляемым компонентом/элементом.
Главным преймуществом такого подхода заключается в том, что стейт синхронизирован с интерфейсом, что позволяет интерфейсу мгновенно реагировать на все изменения
У неуправляемых компонентов/элементов значения полей хранятся прямо в DOM-дереве
Неуправляемые компоненты менее предпочтительны, так как их функционал куда беднее, чем у управляемых.
Единственный компонент, который всегда будет неуправляемым - это инпут typefile, когда пользователь должен загрузить в него какой-то файл.
018 Иммутабельность состояния и собственные события
Что такое иммутабельность?
Иммутабельность - это свойство объекта, при котором его нельзя изменить - как он был создан, таким и остаётся.
Такой подход сохраняется и в React, потому как обычно все наши объекты неизменны. Чтобы поменять отображение объекта на странице, нужно создать его копию и изменить уже её.
Такой подход очень сильно облегчает работу программиста и он обеспечивает работу reconciliation-алгоритма
Однако немного падает производительность за счёт того, что нужно сохранять прошлое состояние объекта
деструктуризация
Тут нужно сказать, что при таком способе записи {...old} все последующие свойства, которые будут после деструктуризации, будут перезатираться. Запись в блоке кода говорит, что свойство increase будет иметь обратное значение тому, что передали через old
findIndex
Так же метод findIndex позволяет нам найти одно определённое значение из массива по нужному нам параметру. Этот метод принимает коллбэк-функцию с условием внутри
Нам нужно реализовать метод для удаления сотрудников из списка сотрудников. Чтобы удалять сотрудников, нам нужно удалять их физически: из данных (например, в БД).
В первую очередь нам нужно создать сам метод для удаления:
У самого работника нам нужно поместить срабатывание метода onDeleted, который будет вызывать его при клике
И в самом списке работников мы завершаем функционал идентификации определённого элемента списка
И теперь при клике на корзинку, мы можем увидеть индекс работника в списке
Дальше нам нужно реализовать непосредственно работу с данными. Самый правильный способ работы с данными - это менять их через стейт.
Далее нам нужно реализовать само удаление элемента со страницы. Сразу нужно повториться: менять напрямую стейт нельзя
Это пример неправильной реализации, так как тут меняется стейт напрямую
Уже подход представленный ниже является верным, так как тут не модифицируется наш стейт, а создаётся новый без удаляемого элемента
Либо можно воспользоваться более коротким вариантом:
Мы сформируем новый массив, в котором будут все элементы, идентификатор которых не равен тому, что передали аргументом в функцию
Практика. Добавление нового сотрудника.
App.js
employees-add-form.js
Для чуть большего ознакомления с иммутабельными объектами, стоит ознакомиться с этой статьёй
019 Практика. Подъём состояния
Подъём состояния (statelifting) - это поднятие внутреннего состояния одного компонента выше по иерархии
В нынешнем варианте, компонент App.js является источником истины, так как данные находятся в нём
Так же данные могут храниться в отдельной сущности или располагаться внутри компонентов
Покажем пример проброса методов вниз:
Создаём методы onToggleIncrease и onToggleRise, которые в качестве пропсов передадим внутрь EmployeesList, откуда мы и сможем получить к ним доступ
App.js
EmployeesList
employees-list-item - этот компонент переведём в функцию и закинем все данные пропсов из нашего компонента выше сюда в этот компонент
Теперь была восстановлена работа печеньки на повышение З/П
Это более сложный и объёмный, но зато более понятный способ создания нового массива
App.js
А вот второй более простой вариант:
App.js
tips
Эти обе записи идентичны и вернут один и тот же результат
Дальше нам нужно реализовать блок подсчёта сотрудников. Для этого нам нужно подсчитать количество записей в массиве. Количество сотрудников с премией можно подсчитать через фильтр
App.js
Это уже сам компонент с общей информацией по сотрудникам (шапка)
app-info.js
Второй метод onToggleRise работает почти идентично тому, что был чуть выше. Нам стоит заняться оптимизацией этих методов, так как внутри них отличается только 2 слова.
App.js
Объединяем метод в один, используя подстановку через props и передаём в EmployeesList этот метод
App.js
Дальше в списке сотрудников нам нужно дата-атрибуты, которые в реакте записываются через data-toggle
employees-list-item
Тут уже нам нужно передать как сам метод, так и атрибут. Атрибут можно передать через ивент, передав в него от текущего таргета атрибут с именем 'data-toggle'
employees-list-item.js
020 React-фрагменты
Очень часто при работе с вёрсткой нам может понадобиться удалить лишний div, в который по правилам JSX мы должны обернуть все элементы возвращаемые из return.
Помешать может лишний див, когда нам нужно использовать компонент при вёрстке как flex-box компонентов, так и grid
Для исправления этой ситуации используют React-фрагменты
Первый способ создания и использования фрагмента подразумевает под собой его импорт и использование в качестве тега:
И теперь можно увидеть, что у нас нет того лишнего дива
Второй способ подразумевает под собой просто использование пустых скобок без импортов фрагмента
Первый способ используется обычно только тогда, когда нам нужно в обёртку передавать какие-либо атрибуты - например, индекс элемента списка
021 Практика. Реализуем поиск и фильтры
Реализация поиска
app.js
search-panel.js
Реализация фильтров на странице
App.js
app-filter.js
022 Семантика и доступность контента (ARIA)
Accessible Rich Internet Applications (ARIA) определяет способ сделать веб контент и веб приложения (особенно те, которые разработаны с помощью Ajax и JavaScript) более доступными для людей с ограниченными возможностями.
Мы должны строить правильную семантическую структуру документа на HTML, чтобы её могли адекватно прочитать скринридеры или, чтобы по ним можно осуществлять навигацию без мышки.
Инлайн-стили в React представляют из себя один большой объект, который мы вкладываем в атрибут style.
Их особенности:
Они, в первую очередь, записываются в один объект
Единицы измерения в них прописываются автоматически (только px)
Все наименования так же идут в camelCase
Вебкиты начинаются с разных регистров букв
Предпочтительнее менять для элементов уже готовые CSS-классы, нежели чем постоянно писать инлайн-стили
024 Стили в React. CSS и SASSSCSS
В первую очередь нам нужно научить вебпак компилировать SCSS, установив плагин
Чтобы подключить sass-файл со стилями, нам нужно его непосредственно подключить к компоненту реакта
Ну и нужно отметить, что если нам нужно импортировать в scss те же переменные из другого scss, то нам придётся импорты делать в каждый файл, а не только в основной
025 Стили в React. Динамические классы и стили
Динамическими классами и стилями называются те классы и стили, которые мы добавили через условие (например, тернарный оператор)
Желательно такие условия выносить в отдельную переменную, чтобы не портить код
026 Стили в React. Styled Components
Styled Components в React позволяют писать CSS код прямо внутри реакт-компонентов
Устанавливаются через:
Дальше для реализации компонента с логикой и его стилями используются ==тэгированные шаблонные строки==
Конкретно тут мы сделаем компонент Button, который будет представлять из себя ссылку a с определёнными стилями внутри кавычек
И вот пример использования стилевых компонентов в React:
Так же эти компоненты можно экспортировать в другие компоненты
Так же компоненты стилей можно наследовать и переопределять для других элементов:
App.js
index.js
Так же мы можем заменить выводимый тег. То есть если мы создавали компонент стилей div, то мы можем его поменять на a или на любой другой тег через as="тег"
index.js
Так же синтаксис поддерживает вкладывание обращений к элементам
App.js
Так же мы можем использовать выражения внутри наших CSS-свойств (те же тернарные операторы, которые будут подставлять нужное значение свойства в зависимости от значения атрибута, которое передаётся через props)
App.js
Несколько важных фактов:
Вендорные префиксы ставятся автоматически - их не требуется писать
Отношения пишутся и работают как в обычном CSS
Псевдоселекторы и псевдоэлементы работают точно так же
Так же можно создавать тут же CSS-анимации
Преймущества:
Инкапсулирование стилей - они нигде друг с другом не пересекаются и нет необходимости писать лишние классы
Так же отпадает необходимость пользоваться БЭМом
Возможность использования пропсов и условий
Вендорные префиксы ставятся автоматически
Минусы:
К такому синтаксису нужно привыкнуть
Очень легко запутаться в тегах, если написано очень много стилизованных компонентов
Названия стилей внутри devtools превращены в кашу (так как идёт динамическая генерация имён классов)
CSS и JS вместе до конца - их не получится отдельно закэшировать
027 Стили в React. Готовые библиотеки компонентов со стилями
Для React существует отдельный и подогнанный под него ==Bootstrap==
Дальше в index.js (самый основной JS-файл) нужно запихнуть файл стилей
И далее в побочных файлах только остаётся импортировать определённые компоненты бутстрапа (из документации)
Использование в основном файле:
028 Поля классов и static
static поля классов:
Вызываются просто через имя класса без инициализации
Можно получить и вывести их значения
Позволяют задать стейт как поле класса (без инициализации через конструктор и ключевого слова static)
029 Заключение модуля
Попробовать сверстать на ReactCofee Shop по макету (уже скопирован в драфты):