REACT - базовые понятия
Что такое React и какие у него особенности?
JS-библиотека для создания пользовательских интерфейсов.
Это не фрэймворк! Cам по себе не позволит создать веб-приложение, предназначен для создания представлений (это «V» в MVC). React – это только представление. Это язык шаблонов + несколько функций, которые позволяют отрисовать HTML. Результат работы React – это HTML.
Разрабатывается и поддерживается Facebook + Instagram и сообществом отдельных разработчиков и корпораций.
Открытый исходный код
Выход первой версии:
- 2009 - Angular
- 2010 - Backbone
- 2011 - Ember
- 2012 - Meteor
- 2013 - REACT
- 2014 - Vue
- 2015 - Polymer
- 2015 - Aurelia
Также Facebook разработал Flux - архитектурный шаблон, который дополняет React (см. Redux).
Ключевые особенности:
- Компонентный подход - разбиваем страницы на небольшие фрагменты. Мы называем эти фрагменты компонентами. Их можно потом переиспользовать в других проектах.
- Виртуальный DOM - React создает кэш-структуру в памяти, что позволяет вычислять разницу между предыдущим и текущим состояниями интерфейса для оптимального обновления DOM браузера.
- Однонаправленная передача данных - свойства передаются от родительских компонентов к дочерним (сверху-вниз). Компонент не может напрямую изменять свойства, но может вызывать изменения через callback функции.
- JSX (JavaScript XML) - расширение синтаксиса JavaScript, "синтаксический сахар" для JS. Позволяет использовать похожий на HTML синтаксис для описания структуры интерфейса. Можно работать с React и без JSX (но не стоит)
Также:
- Декларативный - описываем не поведение, а состояния компонентов (в зависимости от разных данных) + переключаемся между этими состояниями
- Функциональный - поощряется использование функционального подхода к программированию (не ООП)
- Реактивный - как бы из названия видно, что является реализацией реактивного подхода к программированию
- Типизация - динамическая слабая неявная (как и весь JS). Для введения статической типизации можно использовать (в порядке усложнения): propTypes / Flow / TypeScript
- Тестируемость - поддерживает js-тестирование (Jest, Enzyme, Mocha...)
- ES6+ - т.к. всё равно используем Babel, логично использовать возможности ES6 (константы, стрелочные функции, шаблонные строки и т.д.)
- Изоморфность - код может выполняться и на клиентской, и на серверной стороне.
- Быстрый - в частности, потому что выполняет оптимизацию при компиляции кода в JSX -> JS
- Легко интегрировать - React можно встраивать в приложение по частям: часть уже работает на React, часть без него. И всё ок. Можно подключать через CDN и т.д.
- React Native - платформа для разработки мобильных приложений, создает мобильные приложения с помощью React.js. Т.е. на основе React JS приложения можно довольно легко создать мобильное приложение.
Про название
React получил свое название от того, что он реагирует на изменения состояния (хоть и не реактивно, но по графику). Была шутка, что React должен был быть назван Schedule.
Ссылки
- Angular vs. React: Сравнение 7 Основных характеристик
- React и SEO: преимущества изоморфности React для одностраничных приложений
- Все фундаментальные принципы React.js, собранные в одной статье
Ещё разное про React //ToDo - разбирать и дополнять
React JS и React Native
- React JS - js-библиотека для создания UI (2013)
- React Native - платформа для разработки мобильных приложений, создает мобильные приложения с помощью React.js (2015)
Мобильные приложения имеют некоторые преимущества по сравнению с сайтами. Их можно использовать без соединения с Интернетом. Они имеют доступ к таким возможностям устройства, как всплывающие уведомления. Также они позволяют быть в контакте с вашими пользователями в режиме 24/7. React Native — это фреймворк, который позволяет вам создавать мобильные приложения, используя React. Логика приложения пишется на JavaScript, таким образом, программисту не нужно отказываться от привычных приемов веб-разработчика. Все что нужно — научиться писать специфичный для устройства код, который адаптирует компоненты, ранее созданные для веб-сайта к новой среде обитания.
Если мы сравним затраты на разработку разных видов мобильных приложений, мы получим примерно следующие результаты:
- В случае с нативными приложениями вы можете надеяться на довольно высокую производительность, но стоимость разработки будет довольно высокой;
- Если вы предпочтете фреймворки, которые позволяют использовать HTML5, CSS3 и JavaScript, например PhoneGap, вы можете снизить стоимость. Но в этом случае уровень производиетльности будет гораздо ниже;
- В случае React вы можете достигнуть уровня производительности, сравнимого с нативными приложениями. При этом стоимость разработки сравнима с предыдущим примером.
Если вы планируете создать корпоративное веб-приложение и не вполне уверены, будет ли разработка мобильной версии этого же приложения хорошей идеей, вот что вы должны помнить. React Native позволяет использовать уже имеющуюся логику веб-приложения при создании мобильного приложения. Это значит, что команда разработчиков может использовать тот же код, который был использован в процессе создания сайта вместо того, чтобы начинать с чистого листа.
React.js использует Virtual DOM, в то время как React Native использует собственные API
Связывание данных
Связывание данных - функция, которая синхронизирует данные между состоянием (моделью) приложения и представлением.
- Односторонняя привязка данных - любое изменение модели автоматически обновляет представление. Но не наоборот.
- Двусторонняя привязка данных - любое изменение модели автоматически обновляет представление. И наоборот.
В React однонаправленная привязка - свойства передаются от родительских компонент к дочерним ("сверху вниз"). Состояние компонента инкапсулируется и недоступно для других компонентов. Если только оно не передается дочернему компоненту в качестве props (то есть, состояние компонента становится props дочернего компонента).
Компоненты получают свойства как неизменяемые (immutable) значения. Поэтому компонент не может напрямую изменять свойства, но может вызывать изменения через callback функции. Такой механизм называют «свойства вниз, события наверх».
Реактивность
Вообще, определённый подход к созданию кода, парадигма программирования.
Полезно почитать немного про это дело:
- habr - Реактивность в JavaScript: простой и понятный пример
- habr - Основы реактивного программирования с использованием RxJS
Рендеринг на стороне сервера
- Рендеринг на стороне сервера - браузер шлёт серверу запрос, сервер присылает весь HTML-файл. Браузер только выводит его пользователю.
- Рендеринг на стороне клиента - браузер запрашивает у сервера много JS и сырые данные из базы. Генерация HTML идёт в браузере (клиенте).
Рендеринг на стороне клиента имеет недостатки, например плох для SEO. Поэтому можно настроить React для рендеринга на сервере. React может выполняться на стороне клиента и, при этом, рендериться на стороне сервера, и эти части могут взаимодействовать друг с другом. Поэтому он широко используется для создания высокопроизводительных веб-приложений и пользовательских интерфейсов.
React может рендерить компоненты сайта как на серверной, так и на клиентской стороне. Хорош для создания изоморфных приложений - позволяет переиспользовать почти весь клиентский код для рендеринга на сервере, в зависимости от масштаба приложения. Это возможно т.к.: 1) и на сервере и на клиенте используется JS; 2) и там и там не критичен реальный DOM браузера - сам React работает с вирутальным
Изоморфность
При первом обращении к сайту все операции выполняются на сервере и в браузер передается HTML (как обычный статический сайт). После загрузки JS сайт превращается в «одностраничное приложение», и работает соответственно.
Это полезно для SEO + пользователи сразу видят страницу, а не ждут пока загрузятся все данные и отрисуется нужная информация
Когда пользователь открывает сайт, содержимое страницы должно быть загружено с сервера. В случае с SPA, это может занять некоторое время. Во время загрузки пользователи видят либо пустую страницу, либо анимацию загрузки. Учитывая, что по современным стандартам ожидание в течение более чем двух секунд может быть весьма заметным неудобством для пользователя, сокращение времени загрузки может оказаться крайне важным.
Virtual DOM позволяет React легко создавать изоморфные приложения. В других JS-фрэймворках клиентская часть кода часто полагается на DOM браузера, которого нет на серверной стороне => нельзя использовать один код и на клиенте, и на сервере. React же дает нам абстракцию браузерного DOMа в виде виртуального DOMа. Это дает два основных преимущества:
- код, который работает с виртуальным DOM в React не зависит от браузера и может выполняться на сервере;
- React может оптимизировать операции над документами и снизить количество обращений к браузерному DOM и за счет этого значительно ускорить работу фронтенда.
Одной из основных особенностей React является то, что он может выполняться на стороне клиента и, при этом, рендериться на стороне сервера, и эти части могут взаимодействовать друг с другом. Поэтому он широко используется для создания высокопроизводительных веб-приложений и пользовательских интерфейсов.
//ToDo - разобраться какая часть системы отвечает за преобразование JS в HTML? Скорее всего - браузер.
Элемент
Это JS-объект, который описывает узел DOM.
Объектное представление некоторого пользовательского интерфейса.
//Создаём элемент с помощью метода React createElement
const element = React.createElement(
'div',
{id: 'login-btn'},
'Login'
)
//Получаем такой объект (элемент):
{
type: 'div',
props: {
children: 'Login',
id: 'login-btn'
}
}
//Когда он будет отображён в DOM (с помощью функции ReactDOM.render), появится узел DOM
<div id='login-btn'>Login</div>
Компонент, же - класс или функция, которая принимает данные и возвращает элементы.
//компонент Button принимает функцию onLogin и возвращает React элемент
function Button ({ onLogin }) {
return React.createElement(
'div',
{id: 'login-btn', onClick: onLogin},
'Login'
)
}
Надо только учесть, что когда мы работаем с JSX - мы не видим React.createElement
. JSX позволяет писать это проще, но по факту, переделывает в тот самый React.createElement
//Примеры создания React элемента
React.createElement('div', className: 'container', 'Hello!')
<div className='container'>Hello!</div>
<Hello />
Т.е. компонент (класс/функция) генеририрует элемент (js-объект). Этот элемент будет отображён в DOM (с помощью функции ReactDOM.render), и у нас появиться новый DOM узел. А браузер на основе этого DOM-узла отобразит какую-то информацию на экране (кнопку, например)
Ссылки: React Elements против React Components
Компонент(а)
Компонент - класс или функция, которая принимает данные и возвращает элементы React.
Компонент - js-функция, которая возвращает кусок кода, представляющего фрагмент страницы. Описывают DOM-элементы (h1, div, section...). Обычно это части пользовательского интерфейса, которые содержат свою структуру и функциональность. Например: NavBar, LikeButton, или ImageUploader.
Благодаря синтаксису JSX компонента совмещает и разметку, и логику
Концептуально, компоненты подобны JS-функциям - принимают данные (props) и возвращают React-элементы, описывающие что должно появиться на экране.
Когда компонент создан, автоматически появляется тэг <Имя_Компонента /> - при его помощи мы выводим этот JSX в нужном месте.
Т.е. компонент (класс/функция) генеририрует элемент (js-объект). Этот элемент будет отображён в DOM (с помощью функции ReactDOM.render), и у нас появиться новый DOM узел. А браузер на основе этого DOM-узла отобразит какую-то информацию на экране (кнопку, например)
Основополагающая концепция React.js – многоразовые компоненты. Разработчик создает небольшие части кода, которые можно объединять, чтобы сформировать более крупные или использовать их как самостоятельные элементы интерфейса.
Самое главное в этой концепции то, что и большие, и маленькие компоненты можно использовать повторно и в текущем и в новом проекте.
Компоненты, которые были созданы во время работы над тем или иным проектом, не имеют дополнительных зависимостей. Таким образом, ничто не мешает использовать их снова и снова в проектах разного типа. Весь предыдущий опыт может быть с легкостью применен при работе над новым сайтом или даже при создании мобильного приложения.
Ссылки: React Elements против React Components
События //ToDo - доработать
При обработке событий важно понимать, что все атрибуты элементов React именуются с помощью camelCase. При работе с функциями, мы передаем фактическую ссылку на функцию, а не строку.
React.js создает для DOM-события обертку в виде собственного объекта, чтобы оптимизировать производительность работы с событиями. Внутри обработчика все так же возможно получить доступ ко всем методам, доступным для документа.
Как работает React? //ToDo - доработать
В целом, схема работы примерно такая (примерм из PReact)
- Компоненты написанные на JSX (HTML и JS) преобразуются в чистый JS с помощью CLI инструмента Babel.
- После чего React функция “React.createElement” преобразует их в VDOM дерево (ReactElement).
- И наконец, Preact’s VDOM алгоритм создает реальный DOM из VDOM (наше приложение).
JSX это конечно круто, он помогает написать “представление” DOM, но в конечном счете, нам нужен именно реальный DOM. Нам надо преобразовать наше “представление” в JSON (VDOM, который также дерево), чтобы в дальнейшем использовать его в качестве входных данных для создания DOM. Для этого React использует функцию React.createElement.
Для того, чтобы преобразовать JSX к виду “React.createElement” функции нам понадобиться Babel. Он проходит через каждый узел JSX и преобразует его к “React.createElement” функции. Т.е. на входе у нас JSX, на выходе из Babel - выозов функции React.createElement() в которую передан чистый JS. Те JSX -> React.createElement(JS)
React.createElement возвращает ReactElement - он является простым JS объектом узла DOM с его свойствами и наследниками.
Функция React.createElement не создает целое дерево! Она создаёт простой JS объект для одного узла.
!
- VNode = ReactElement
- h = React.createElement
Сценарий 1: При первом запуске
- Когда наше приложение загружается в первый раз, React создаёт ReactElement с наследниками и атрибутами для основного компонента.
- Потом мы создаём реальный DOM для родительского узла
- Повторяем эти действия (создание реального DOM) для всех наследников
- Добавление наследников к родителям
- Обработка дочерних компонентов наследников
- Повторяем цикл для каждого дочернего узла.
- И наконец последний пункт. Здесь мы просто вызываем “componentDidMount” для всех компонентов (дочерних и родительских).
После того, как все сделано, ссылка на реальный DOM добавляется к каждому экземпляру компонента!. Данная ссылка используется для всех остальных действий (создание, обновление, удаление), чтобы сравнить и избежать создание тех же DOM узлов.
Сценарий 2: Удаление узла из DOM, который не имеет наследников
- После первоначального рендеринга, каждое изменение считается “обновлением”. Цикл обновления работает почти так же как и цикл создания
- Отличие в том, что это приводит к вызову “componentWillReceiveProps”, “shouldComponentUpdate”, и “componentWillUpdate” для каждого компонента.
- Кроме того, цикл обновления, не создаёт повторно элементы которые уже присутствуют в DOM. Чтобы избежать повторное создание узлов в реальном DOM, каждый экземпляр компонента имеет ссылку на реальное DOM дерево, которое было создано во время начальной загрузки. И когда ReactElement создан, каждое его свойство сравнивают с узлами в реальном DOM. Если такой узел уже существует, цикл переходит к следующему узлу.
- Мы удаляем элемент из VDOM. Происходит сравнение с реальным DOM (скорее всего, с его копией, чтоб не читать реальный DOM, т.к. это дорогая операция). Видим, что они различаются. Перерисовываем реальный DOM
- Вызываем componentDidMount
Сценарий #3: Удаление/Unmounting компонента
- Удаление компонента похоже на удаление одного узла. За исключением того, что мы удаляем узел, который имеет ссылку на компонент, в этом случае библиотека вызовет “componentWillUnmount”, а затем рекурсивно удалит все дочерние элементы DOM.
Babel
Транспилятор. Преобразует JSX в обычный JS
Компоненты написанные на JSX (HTML и JS) преобразуются в чистый JS с помощью CLI (интерфейс командной строки) инструмента Babel
Транспилирование — это конвертация кода в другой, похожий язык. Это важная часть фронтенд-разработки: поскольку в браузерах медленно появляются новые фичи, были созданы языки с экспериментальными возможностями, которые транспилируются в совместимые с браузерами языки.
Webpack
Сборщик модулей, bundler. Используется для компиляции JS-модулей.
Берёт всё, от чего зависит проект (Css, JS...), и преобразует это в статические ресурсы, которые могут быть переданы клиенту. Позволяет объединять ресурсы и библиотеки, необходимые для проекта, в один файл - «bundle» (пачка).
Как уже говорилось, node.js — это среда исполнения JavaScript, разработанная для запуска на серверах. Вот как сначала выглядело использование node.js-модулей: вместо загрузки всего moment.min.js в скриптовом теге HTML можно было грузить JS-файл напрямую:
var moment = require('moment');
console.log("Hello from JavaScript!");
console.log(moment().startOf('day').fromNow());
Загрузка модулей работает прекрасно, поскольку node.js — это серверный язык с доступом к файловой системе. Также ему известно расположение всех npm-модулей, поэтому вместо require('./node_modules/moment/min/moment.min.js) можно писать просто require('moment').
Всё это прекрасно, но если вы попробуете использовать приведённый код в браузере, то получите ошибку, в которой говорится, что require не определён. У браузера нет доступа к файловой системе, поэтому такая загрузка модулей реализована очень хитро: файлы нужно грузить динамически, синхронно (замедляет исполнение) или асинхронно (могут быть проблемы с синхронизацией).
И здесь появляется бандлер (bundler). Это инструмент для сборки модулей в единые пакеты, имеющий доступ к файловой системе. Получающиеся пакеты совместимы с браузером, которому не нужен доступ к файловой системе. В нашем случае бандлер нужен для поиска всех выражений require (имеющих ошибочный, с точки зрения браузера, JS-синтаксис) и замены на настоящее содержимое каждого требуемого файла. В финале мы получаем единый JS-файл без выражений require!
Самым популярным бандлером сначала был Browserify, выпущенный в 2011 г. Он был пионером в использовании node.js-выражений require во фронтенде (это позволило npm стать самым востребованным диспетчером пакетов). К 2015-му лидером стал webpack (ему помогла популярность фронтенд-фреймворка React, использующего все возможности этого бандлера).
Ссылки
- habr - Объясняем современный JavaScript динозавру
- Как работает Virtual DOM ?
- csssr - Основы производительности React-приложений
Virtual DOM //ToDo - доработать
Это дерево React элементов на JavaScript.
На самом деле React хранит два Virtaul DOM - тот, который отражает текущее состояние React и тот, который отражает текущее состояние DOM. React сравнивает их между собой, не обращаясь к реальному DOM без крайней необходимости (т.к. чтение реального DOM - ресурсоёмкая операция)
React отрисовывает Virtual DOM в браузере, чтоб сделать интерфейс видимым. React следит за изменениями в Virtual DOM и автоматически изменяет DOM в браузере так, чтоб он соответствовал виртуальному.
Виртуальный DOM обновляет требуемую часть, сравнивая различия между предыдущей и текущей версией HTML. Процесс схож с тем, как Github работает при обнаружении изменений в файле.
React создает кэш-структуру в памяти, что позволяет вычислять разницу между предыдущим и текущим состояниями интерфейса для оптимального обновления DOM браузера.
React считается быстрым из-за VirtualDOM. В компоненте есть метод render, который вызывается при каждом обновлении компонента. Затем результат рендера (здесь и далее под рендером будет иметься в виду именно вызов функции render компонента, а не рендер в реальный DOM) обрабатывается React, сравнивается результат текущего рендера с результатом предыдущего и в реальный DOM вносятся только необходимые изменения, а не целиком. Учитывая, что операции с реальным DOM медленные, это должно быть быстрее.
Но, операции с Virtual DOM тоже могут быть медленными. Результат рендера Реакта — это многоуровневый объект. Сравнение результатов рендера — это не сравнение двух объектов по ссылке, а их полное, глубокое сравнение.
Более того, React будет делать полный перерендер компонента при любом вызове SetState (даже, если данные не поменялись) и при перерендере родителя. То есть, если у вас большое приложение, и вы вызываете setState у корневого компонента, у вас всё приложение целиком будет перерендерено. Реакт построит VDOM для всего приложения, сравнит его с предыдущим результатов и в DOM поместит те самые незначительные правки (если они даже были). Всё это приведет к значительным потерям в производительности приложения.
Это решается черезе shouldComponentUpdate
и PureComponent
. Тут тоже есть две проблемы:
-
Если в стейте есть ссылочные типы. Если мутировать объект, то нет никакой возможности проверить, изменилось ли значение, так как объект в текущем и новом стейте будет ссылаться на один и тот же объект. Компонент не будет перерендериваться, хотя данные в действительности поменялись.
Решение: либо заменяем все мутабельные операции на аналогичные иммутабельные операции, либо создаем новую ссылку и затем уже её мутируем. -
Обратная ситуация. Когда мы каждый раз создаем ссылочный тип данных, даже если данные в нем не поменялись. Например, прогоняем данные через map - на выходе всегда новый массив, даже если данные не изменились. Получается, что у нас каждый раз создается новая ссылка, компонент будет перерендериваться, хотя не должен.
Решение: мемоизация (разновидность кэширования). Запоминаем предыдущие результаты вызова функции, и если вызывается снова - используем их из кэша. Есть специальные библиотеки с разной реализацией.
Важен тот момент, что так как значение берется из кеша, то возвращается та же самая ссылка. И так как у нас все данные иммутабельны, то нужна мемоизация, которая будет проверять значения по ссылке, а не глубоким сравнением
Также многие забывают, что использование стрелочных функций, bind и литералов массивом \ объектов в рендере создает новую ссылку при каждом рендере. Решается использованием bind один раз в конструкторе или использованием свойств класса и выносом литералов за пределы рендера. См. подробнее: csssr - Основы производительности React-приложений
Когда мы создаем динамичную интерактивную веб-страницу, мы хотим, чтобы DOM обновлялся так быстро, как это возможно после изменения состояния определенного элемента. Для данной задачи некоторые фреймворки используют прием, который называется «dirty checking» и заключается в регулярном опросе состояния документа и проверке изменений в структуре данных. Как вы можете догадаться, подобная задача может стать самой настоящей головной болью в случае высоконагруженных приложений. Virtual DOM, в свою очередь, хранится в памяти. Именно поэтому в момент, когда «настоящий» DOM меняется, React может изменять Virtual DOM в мгновение ока. React «собирает» такие изменения сравнивает их с состоянием DOM, а затем перерисовывает изменившиеся компоненты.
При данном подходе вы не производите регулярное обновление DOM. Именно поэтому может быть достигнута более высокая производительность React приложений.
Изоморфность Кстати, Virtual DOM позволяет React легко создавать изоморфные приложения. В других JS-фрэймворках клиентская часть кода часто полагается на DOM браузера, которого нет на серверной стороне => нельзя использовать один код и на клиенте, и на сервере. React же дает нам абстракцию браузерного DOMа в виде виртуального DOMа. Это дает два основных преимущества:
-
код, который работает с виртуальным DOM в React не зависит от браузера и может выполняться на сервере;
-
React может оптимизировать операции над документами и снизить количество обращений к браузерному DOM и за счет этого значительно ускорить работу фронтенда.
Про DOM Входными данными для render() являются свойства (props) и внутреннее состояние, которое может быть обновлено в любое время.
Когда для render меняются входные данные, меняется и результат выполнения render.
React.js ведет запись жизненного цикла компонента. Когда React.js видит, что один рендер отличается от другого, он переводит разницу между своим виртуальным представлением в операции с DOM API, которые будут отрисованы в документе.
Ссылки
-
Оф. документация - Виртуальный DOM и детали его реализации в React
-
React и SEO: преимущества изоморфности React для одностраничных приложений
-
Props (свойства)
Произвольные данные, которые принимают компоненты. Это могут быть и функции.
Информация, коллективно используемая родительским компонентом и компонентами-потомками.
Они передаются в качестве аргументов компонента. Выглядят так же, как атрибуты HTML.
Все данные в компонент приходят в первом аргументе. Обычно он называется props (от properties).
Любой компонент может принимать параметры, которые потом использует внутри себя. Например, текст, который надо вывести внутри JSX-разметки, генерируемой компонентом. Главный такой параметр называется props (это просто название параметра, оно принято в React). Т.е. React, вызывая компонент, всегда передаёт в этот параметр props некий объект. Находясь внутри компонента я могу получать данные, которые пришли внутри этого props.
//Тут передаются props imageURL (значение - адрес), caption (текст), isPlaying (функция)
<Button imageURL='http:tinyurl.comlkevsb9' caption='New York!' isPlaying={this.state.isMusicPlaying} />
Props и производительность
Основная опасность - увеличение количества render, которые мешают производительности
Важно: при вызове render() перерисовывается не только родительский компонент, но и все дочерние (хотя, в них свойства могли и не поменяться). Т.е. если у родительского компонента внутри render есть дочерние компоненты - они будут перерисовываться. Соотвественно, если мы вызываем render на родительском компоненте - перерисуем всё приложение. Чтоб решить этот вопрос - используем создание компонента от PureComponent
Даже если я создал класс от PureComponent - это не гарантирует отсутсвие лишних ренедров при тех же данных. Одна из причин - анонимные функци (они при каждом рендере новые).
//Неверно:
render() {
return <Component onClick= {() => this.hangleClick}>
}
//Верно:
handleClick = () => {...}
render() {
return <Component onClick= {this.hangleClick}>
}
То же самое с объектами - не создаём их прямо в функции, а подключаем как константу
//Неверно:
render() {
return <Component test= {{a: 1}}>
}
//Верно:
const obj = {a:1}
render() {
return <Component test= {obj}>
}
Ссылки
State (состояние)
Специальный js-объект внутри компонента. Хранит данные, которые могут изменятся с течением времени.
Описывает внутреннее состояние компонента.
- Props - входные данные, которые передаются в компонент извне.
- State - хранит данные, которые создаются в компоненте и полностью зависят от компонента. Определяется внутри компонента и доступно только из компонента. Компонент может передать своё состояние дочерним компонентам в виде props.
Также, в отличие от props, значения в state можно изменять.
С добавлением Redux всё становится немного интереснее, т.к. Redux вводит свой state. Он собственно и нужен для управления state приложения.(см. Redux)
Как правильно использовать state
- Не изменяйте состояние напрямую. Вместо этого используйте setState()
// Неправильно
this.state.comment = 'Привет';
// Правильно
this.setState({comment: 'Привет'});
- Обновления состояния могут быть асинхронными. Поскольку this.props и this.state могут обновляться асинхронно, вы не должны полагаться на их текущее значение для вычисления следующего состояния.
- Обновления состояния объединяются. Когда мы вызываем setState(), React объединит аргумент (новое состояние) c текущим состоянием. Состояния объединяются поверхностно.
Ссылки
Композиция в React
Комбинирование меньших компонентов при формировании большего.
Reverse Data Flow - обратный поток данных
Изменение state родительского компонента из его компонента-потомка.
Например такая картина:
- есть два компонента: Родитель и Потомок
- в Родителе хранится состояние (например цвет фона)
- в Потомке происходит что-то, что должно поменять состояние Родителя (например, нажата кнопка)
- Как из дочернего компонента повлиять на состояние родительского?
Делаем так:
- в Родителе кроме state определяем функцию для изменения этого state
- эту функцию передаём в виде колбэка Потомку (через props)
- Потомок вызывает колбэк, тот отрабатывает в Родителе, и там меняется состояние
Другими словами
В React обычным является кода родитель управляет потомком. Так же часто надо управлять родителем из дочернего компонента. Такой подход как раз и называется "Обратный поток данных".
В родительском компоненте, там где хранится состояние, хранится и обработчик этого события. Нужно только передать этот обработчик в дочерний компонент в качестве props. В дочернем компоненте, в нужный момент я просто подставлю эту функцию из props, и отработает изменение состояния в родительском компоненте.
Ссылки
State lifting - подъём состояния
Перенос данных / функций из дочернего элемента в родителя, чтоб они были доступны нескольким потомкам. А из потомка вызываю эти обработчики как коллбэки (через props) или получаю эти данные в виде props.
Если несколько компонентов должны отражать одни и те же изменяющиеся данные - поднимаем общее состояние до ближайшего общего предка.
Ссылки - Оф. документация - Подъём состояния
Key //ToDo - доработать
Атрибут, особое уникальное свойство элемента. Связывает данные и элементы React.
Если быть точным - это special props. Не включается в объект props и недоступно внутри самого компонента.
Например, если надо удалить одну статью из 10 - React удалит только статью с нужным key, а остальные перестраивать не будет.
Ключи помогают React идентифицировать, какие элементы были изменены, добавлены или удалены. Ключи должны быть заданы элементам внутри массива, чтобы предоставить элементам постоянный идентификатор.
Ключи оптимизируют работу с элементами массивов, уменьшают количество ненужных удалений и созданий элементов.
При использовании ключей важно понимать, что при смене данных ключи должны меняться. Яркий пример ошибки- использование индекса элемента в массиве как key. Но массив изменится, а индекс останется тот же...
Лучший способ выбрать ключ — использовать строку, которая однозначно идентифицирует элемент списка среди его соседних элементов. Идеальные ключи должны браться из самих данных - id, уникальный title или ещё что-то в этом роде.
Главная задача ключей в реакте — помогать механизму reconciliation. Без key механизм reconciliation сверяет компоненты попарно между текущим и новым VDOM. Из-за этого может происходить большое количество лишних перерисовок интерфейса, что замедляет работу приложения. Добавляя key, вы помогаете механизму reconciliation тем, что с key он сверяет не попарно, а ищет компоненты с тем же key (тег / имя компонента при этом учитывается) — это уменьшает количество перерисовок интерфейса. Обновлены/добавлены будут только те элементы, которые были изменены/не встречались в предыдущем дереве.
Ссылки
- Оф. документация - Списки и ключи
- Оф. документация - Согласование
- habr - подробная статья
- YouTube - Отображение массивов, смысл аттрибута key
Refs //ToDo - доработать
Аттрибут HTML-элемента или классового компонента.
Если быть точным - это special props. Не включается в объект props и недоступно внутри самого компонента.
От reference - ссылка.
Используются для получения ссылки на узел DOM или компонента в React. Refs возвращает ссылку на элемент. Почти как старые добрые getElementById.
React.JS refs нужен чтоб понадобилось «достучаться» к конкретному элементу и вызвать метод. Добавляем атрибут ref в компонент для обратного вызова.
Полезно в нескольких случаях.
Например:
- вы хотите прочитать значение элемента без React
- навесить jQuery библиотеку на элемент
- вызвать какой-то нативный метод - например focus.
В обычном потоке данных React родительские компоненты могут взаимодействовать с дочерними только через пропсы. Чтобы модифицировать потомка, вы должны заново отрендерить его с новыми пропсами. Но, могут возникать ситуации, когда вам требуется императивно изменить дочерний элемент (React-компонентом или DOM-элемент), обойдя обычный поток данных. Для этого есть refs
Обычно рефы присваиваются свойству экземпляра класса в конструкторе, чтобы на них можно было ссылаться из любой части компонента. Refs становятся доступны после метода render и перед componentDidMount.
Почему бы не брать ссылку по ID элемента?
- Под каждый элемент должна быть уникальная id, тогда как названия ref могут повторяться в разных компонентах.
- Это противоречит “философии” React.
Best Practise
- Выносите функции для получения ref в методы
- Не используйте string рефы (старый синтаксис ref)
Избегаем использования Старайтесь не использовать, если в них не реальной необходимости.
Ref отличный способ доступа к DOM элементам, но его нужно применять с осторожностью. Это не React way, а просто возможность доступа к DOM элементам. Если это возможно - лучше использовать state или props вместо refs, так как они поддерживают правильный поток данных в приложении, а refs нет.
Избегайте использования рефов в ситуациях, когда задачу можно решить декларативным способом. Например, вместо того чтобы определять методы open() и close() в компоненте Dialog, лучше передавать ему проп isOpen
Применение оправдано
- Управление фокусом, выделение текста или воспроизведение медиа.
- Анимации.
- Интеграция с DOM библиотеками.
Ссылки
Среда React
Для работы используется:
- NPM/Yarn - для управления зависимостями. Ну, и чтобы установить Create React App (для работы npm нужен Ruby)
- Babel
- WebPack
- WebPack server
- Create React App - специальная npm/yarn утилита дл ябыстрого развоарчивания проекта на React. Содержит Babel, WebPack и прочее
- OpenServer не нужен!
Вообще, можно работать без всего этого добра - добавить в код ссылку на CDN Реакта и всё. Но с "добром" удобнее.
Create React App
Специльное приложение от разработчиков React. По сути - готовая среа для работы, с настроенным Babel, WebPack, live development server, линтерами и всеми чудесами. Установил - и можешь сразу начинать работать.
GitHub
React + Bootstrap
C Bootsrap в React можно работать как в чистом виде, так и при помощи специальных react-библиотек, которые интегрируют Bootstrap в React.
Алгоритм мыследеятельности при создании React-приложения
- есть некий дизайн (UI)
- глядя на него, я начинаю общаться с заказчиком, и разбираться - какие данный приходят на ту или иную страницу, какие с ними действия происходят, и т.д.
- на основе этого я формирую state для каждой из страниц. Формирую BLL - Busines Logic Layer
- параллельно решаю вопрос как буду управлять state (state managment) - например, через Redux
- потом начинаю кодить компоненты UI и связывать их со state
- ну, и тестирование
устанавливая пакет - дописывать в конце --save Означает, что нужно внести запись в package.json
npm install react-router-dom --saveМетоды отладки React
- React devtools поставляется в двух видах
- отдельным пакетами
- расширением для популярных браузеров. В расширении можно увидеть изменения состояний приложения и узлы виртуального DOM-дерева.
- console.log() Иногда хочется отлаживать по старинке, с помощью console.log(). Можно получить значение переменной внутри JSX прямо в точке её применения. <img src={console.log('logo', logo) || logo} /> Как это работает: console.log() вернет undefined и код выполнится дальше по условию "||", а в консоли браузера мы увидим искомое значение, например: "/static/media/logo.5d5d9eef.svg".
- (() => { debugger })() || // anything
- Отладка внутри IDE WebStorm
- Установите расширение Chrome — JetBrains IDE Support.
- Добавьте Run/Debug-конфигурацию.
- Запустите create-react-app через терминал командой: $ yarn start
- Выберите конфигурацию Debug и нажмите кнопку с иконкой жука (в правом верхнем углу IDE)
- Откроется браузер с предупреждением "JetBrains IDE Support отлаживает этот браузер". Замечено, что если теперь открыть Chrome DevTools по [F12], то отладка в WebStorm завершится — не надо этого делать)
- можно отметить нужную строчку кода, как точку останова, затем перегрузить страницу браузера по [F5], и получить желаемое — инструмент отладки внутри WebStorm.
В React мы никогда не лезем в DOM напрямую.
Никаких getElementById и т.д. Мы работаем с VirtualDOm, а уже сам React занимается связкой Virtual DOM & DOMИзбегать циркулярных (циклических) зависимостей
когда , например, файл a.js импортирует в себя файл b.js, и при этом внутри b,js есть импорт файла a.js. То есть фалы импортируются друг в друга. Это говнокод, идущий в разрез с принципами функционального программирования. Т.е не должно быть именно взаимных импортов. Но, можно вызвать функцию из другого файла, и в качестве данных отдать в неё свою функцию. Т.е. использовать callback. https://www.youtube.com/watch?v=iN6QXbHedQcКак правильно получать данные из html-элемента (без использования ref)
let onQuoteChanged = (event) => { let text = event.target.value; }; return ( <textarea onChange={onQuoteChanged} /> }Избегать изменения элементов/данных по ссылке
https://www.youtube.com/watch?v=NhT5nMvve4Q
мы не знаем где ещё он используется.
работать надо с локальными переменными.
Никогда не менять внешние переменные, и тем более - ничего, что приходит в props.
Работать с иммутабельными данными (теми, которые не меняются по ссылке). Если нужно поменять что-то - мы не меняем по ссылке локальную переменную (например массив), а создаём новый массив с нужными параметрами
-
Про JS-Объекты (и массивы). При копировании объект в памяти остаётся то же, на него просто создаётся новая ссылка. Поэтому, если измнеить что-то в объекте-копии, оригинальный объект тоже изменится (т.к. у нас есть только один объект, с двумя разными ссылками на него). Если объект одноуровневый - можно сделать его полноценную копию так (через спред-оператор): newObject = {...oldObject} Но, если в объекте oldObject были вложенные объекты/массивы - они передадутся опять по ссылке, а не создадут полноценной копии.
При работе с функциями, мы передаём фактическую ссылку на функцию, а не строку.
Передача аргументов в обработчики событий
Внутри цикла часто нужно передать дополнительный аргумент в обработчик события. Например, если id — это идентификатор строки, можно использовать следующие варианты:
<button onClick={(e) => this.deleteRow(id, e)}>Удалить строку <button onClick={this.deleteRow.bind(this, id)}>Удалить строку
Две строки выше — эквивалентны, и используют стрелочные функции и Function.prototype.bind соответственно.
В обоих случаях аргумент e, представляющий событие React, будет передан как второй аргумент после идентификатора. Используя стрелочную функцию, необходимо передавать аргумент явно, но с bind любые последующие аргументы передаются автоматически.
Ссылки
Тестирование
Есть unit-тестирование и e2e тестирование. Разбираться.
Вроде, в первую очередь, все говорят про unit-тесты
Популярные варианты
- Jest - delightful JavaScript testing used by Facebook to test all JavaScript code including React applications. Разрабатывается Facebook
- Enzyme - a JavaScript Testing utility for React that makes it easier to assert, manipulate, and traverse your React Components’ output.
- Mocha
На оф. сайте также упоминаются
- react-testing-library - Simple and complete React DOM testing utilities that encourage good testing practices.
- React-unit - a lightweight unit test library for ReactJS with very few (js-only) dependencies.
- Skin-deep - Testing helpers for use with React’s shallowRender test utils.
- Unexpected-react - Plugin for the unexpected assertion library that makes it easy to assert over your React Components and trigger events.
Ссылки Википедия - модульное тестирование Оф. документация - инструменты тестирования (en) habr - Тестирование React-Redux приложения (Jest) Что и как тестировать с помощью Jest и Enzyme. Полная инструкция по тестированию React-компонентов Тестирование компонентов в React с использованием Jest: основы learn.javascript.ru - Автоматические тесты при помощи chai и mocha Знакомство с разработкой через тестирование в JavaScript (Mocha) Hexlet - Тестирование JSReact (платный доступ) Пацианский М - Тестирование React компонентов с помощью jest и enzyme Модульное тестирование React-приложения с помощью Jest и Enzyme
Ссылки:
- Оф. документация - обучение теоретическое
- Оф. документация - обучение практическое
- IT Kamasutra - лучший курс видео. Большой
- http://code.mu - курс. Должен быть неплох
- learn.javascript.ru - вводный курс видео. Короткий
- habr- Учебный курс по React, (28 частей)
- monsterlessons.com - вводные уроки. Видео + текст. Примерно 2017
- какой-то курc
- ещё курc
- Максим Пацианский
- npm trends - сравнение популярности React, Angular & Vue
- google trends - сравнение популярности React, Angular & Vue
REACT - компоненты
Названия компонент начинаются с Заглавной буквы
Это важно, так как в работе будут сочетаться HTML-элементы и элементы React.
Названия со строчных букв зарезервированы для HTML. Если вы попробуете назвать элемент просто button, при рендере фреймворк проигнорирует его и отрисует обычную HTML-кнопку.
Компоненты = чистые функции
React-компоненты обязаны вести себя как чистые функции по отношению к своим пропсам.
Чистые функции не пытаются ничего изменить и всегда отдают тот же результат (при условии, что на вход подаются одни и те же данные).
Функция не должна работать ни с какими глобальными объектами или генерировать данные. Всё с чем она работает - только с тем, что приходит в неё через props (т.е. через параметры функции).
Такие функции не меняют свои входные данные и предсказуемо возвращают один и тот же результат для одинаковых аргументов. Возвращаемая разметка должна зависеть только от входящих значений props - если 100 раз вызвать функцию с одними и теми же значениями props, мы 100 раз получим один и тот же результат.
State даёт компонентам возможность реагировать на действия пользователя, ответы сервера и другие события, не нарушая чистоту компонента.
Как поддреживать чистоту компонент:
-
Никогда не менять по ссылке внешние (глобальные) переменные, массивы и т.д.
Не сортировать и вообще не трогать. Особенно то, что приходит в props.
Если надо изменить - создавай в компоненте отдельную переменную, записывай в неё, и её меняй. -
Пропсы можно только читать!
Компонент никогда не должен что-то записывать в свои пропсы — вне зависимости от того, функциональный он или классовый.
Пример чистой функции - не меняет свои входные данные и предсказуемо возвращает один и тот же результат для одинаковых аргументов:
function sum(a, b) {
return a + b;
}
Пример нечистой функции — записывает данные в свои же аргументы:
function withdraw(account, amount) {
account.total -= amount;
}
Смотри подробнее в секции "Чистые функции"
Ссылки
Компоненты и Props //ToDo - доработать
Произвольные данные, которые принимают компоненты. Это могут быть и функции.
Информация, коллективно используемая родительским компонентом и компонентами-потомками.
Они передаются в качестве аргументов компонента. Выглядят так же, как атрибуты HTML.
Все данные в компонент приходят в первом аргументе. Обычно он называется props (от properties).
Любой компонент может принимать параметры, которые потом использует внутри себя. Например, текст, который надо вывести внутри JSX-разметки, генерируемой компонентом. Главный такой параметр называется props (это просто название параметра, оно принято в React). Т.е. React, вызывая компонент, всегда передаёт в этот параметр props некий объект. Находясь внутри компонента я могу получать данные, которые пришли внутри этого props.
//Тут передаются props imageURL (значение - адрес), caption (текст), isPlaying (функция)
<Button imageURL='http:tinyurl.comlkevsb9' caption='New York!' isPlaying={this.state.isMusicPlaying} />
Чтоб при вызове компонента передать что-то в его props - достаточно прописать некий атрибут, например name='Dima' превратится в props.name
const User = (props) => {
return (
<p> {props.name} </p>
<p> {props.age} </p>
)
}
<User name='Dima' age='30' />
UNSORTED
Props = объект, который позволяет передать в компонент какие-то данные. Или предать callback - функцию, которая компонента потом сможет запустить. Т.е. сама функцию создана в одном файле, но если передать её через props - вызывается из другого файла
Правильный подход - прокидывать в компоненты как можно меньше лишней информации props. Чтоб компоненты оставались "чистыми", "презентационными". Чтоб, в идеале, компонента ничего не знала про store - чтоб мы никак не были завязаны на store, тогда в будущем можно использовать эти компоненты с другими реализацями state-managmant (MobX, ...)
Первый аргумент функции, создающей компонент. В него приходят все данные, с которыми мы работаем в компоненте.
Каждый элемент имеет список свойств (атрибутов), как и в HTML. В Реакте это называется props.
Пропсы можно только читать!
Компонент никогда не должен что-то записывать в свои пропсы — вне зависимости от того, функциональный он или классовый.
Ссылки
Компоненты контейнерные и презентационные (умные/глупые) ToDo - доработать
Два типа компонент по типу задач, которые выполняют.
Контейнерная
Отвечает за данные и операции с ними. Например, берёт на себя общение со Redux или AJAX запросы.
Позволяет поддерживать внутреннюю (глупую) компоненту чистой.
Всегда работают как обёртка вокруг другой компоненты - контейнерной или презентационной.
Обычно не содержат разметки и не имеют CSS-стилей. Их задача - делать грязную/сложную работу (запросы и т.д.), а не отрисовывать данные.
Их часто создают с использованием react-redux, они могут осуществлять диспетчеризацию действий Redux.
Презентационная
Отвечает лишь за отрисовку полученных данных. И передачу в систему данных от пользователя (клик мышкой, ввод текста...)
Не осведомлены о состоянии Redux и прочем. Обычно содержат DOM-разметку (JSX, HTML...)
Часто обрачиваются контейнерными компонентами, которые берут на себя общение со Store и прочую логику. А презентационная компонента остаётся чистой, мало знает об окружении, не сильно с ним связана => может быть легко переиспользована в другом проекте. И легко протестированна.
Могут быть обёрнуты контейнерной компонентой, либо работают без них (сами по себе)
Получают данные через свойства и могут вызывать коллбэки, которые также передаются им через свойства. Не должны изменять данные.
Подробнее:
- Отвечают за внешний вид
- Могут содержать как другие presentation компоненты, так и контейнеры
- Поддерживают слоты (Often allow containment via this.props.children)
- Не зависят от приложения (Redux и т.д.)
- Не зависят от данных
- Интерфейс основан на props
- Часто stateless, т.е. не имею своего состояния
- Часто функциональные
UNSORTED
Контейнерная компонента - обёртка вокруг презентационной компоненты, чтоб сохранить её чистой (не зависимой от props, store, state...)
Тогда презентационную можно будет пере-использовать в других проектах и т.д.
Т.е. чтоб презентационная компонента не использовала actionCreator из dispatch и прочее.
Вместо этого мы всё получаем через props, а вместо dispatch используем callbacks.
С другой стороны, если бы контейнерной компоненты не было - нам нужно было бы каждый callback прокидывать из store через всё дерево в каждую презентационную компоненту. Это неудобно. Поэтому мы до контейнерной компоненты прокидываем обычный dispatch + state, в ней вызываем отрисовку чистой презентационной компоненты, и передаём ей (через props) из этого dispatch колбэки и state.
Контейнерная компонента общается со Store через context API (https://ru.reactjs.org/docs/context.html)
-
Контейнерная компонента - берёт на себя общение со Store (ООП-объект, хранящий state). И позволяет поддерживать внутреннюю компоненту чистой Прокидывает в неё данные из Store (props, dispatch колбэки) отрисовывает презентационную компоненту
-
Если кратко, компоненты-контейнеры отвечают за данные и операции с ними. Их состояние передается в виде свойств в компоненты-представления и отображается.
- Организация контейнерных компонент и AJAX
- снаружи - контейнерная, которая через connect работает со Store
- в ней (в том же файле) - классовая, которая делает AJAX-запросы и прочие сайд-эффекты
- классовая вызывает отрисовку функциональной (которая лежит в отдельном файле). Та получает только props и отдаёт JSX
Презентационная компонента - чистая компонента, получает props, отдаёт JSX. Всё получает через props (из контейнерной компоненты), а вместо dispatch используем callbacks.
- smart-компоненты - манипулируют данными
- dumb-компоненты - что-то отрисовывают
Пакет react-redux предоставляет привязки React для контейнера состояния Redux, чрезвычайно упрощая подключение React-приложения к хранилищу Redux. Это позволяет разделять компоненты React-приложения, основываясь на их связи с хранилищем. А именно, речь идёт о следующих видах компонентов:
- Презентационные компоненты. Они отвечают лишь за внешний вид приложения и не осведомлены о состоянии Redux. Они получают данные через свойства и могут вызывать коллбэки, которые также передаются им через свойства.
- Компоненты-контейнеры. Они ответственны за работу внутренних механизмов приложения и взаимодействуют с состоянием Redux. Их часто создают с использованием react-redux, они могут осуществлять диспетчеризацию действий Redux. Кроме того, они подписываются на изменения состояния.
Ссылки:
- Презентационный компонент и контейнер в React
- Stateful и Функциональные stateless компоненты в React
- По поводу паттернов в React
- habr - Паттерны React
Компоненты с состоянием и без (stateful/stateless)
Некоторые компоненты используют в React метод setState(), а некоторые нет.
- Stateful - компоненты имеющие состояние (state). Всегда являются классовыми компонентами (у функциональных своего state нет).
- Stateless - компоненты без состояния. Могут быть и функциональными и классовыми.
Недостатки stateful-компонент
- Наличие состояния затрудняет тестирование компонентов
- Наличие состояния затрудняет понимание работы компонента
- Наличие состояния слишком легко позволяет вставить в компонент бизнес-логику. А это не хорошо
- Наличие состояния затрудняет обмен информацией с другими частями приложения. Состояние легко передаётся вниз (потомкам), а вот в стороны - намного труднее
Ссылки:
- Stateful и Функциональные stateless компоненты в React
- По поводу паттернов в React
- habr - Паттерны React
Компоненты классовые и функциональные //ToDo - доработать
Функциональные (Functional components) - простые компоненты, созданные как функции.
React - функционально-ориентированная библиотека, так что рекомендуется использовать эти компоненты, там где можно. Кстати, они появились позже
//Создаются так:
const Welcome = (props) => {}
//Или так:
function Welcome(props) {}
Классовые (Class-Components) - объявлены как класс, имеют методы жизненного цикла, локальное состояние, refs и многие другие штуки.
Без лишней необходимости их лучше не использовать. С появлением хуков вообще становятся менее востребованны.
//Создаются так:
class Welcome extends React.Component {}
//Или так:
class Welcome extends React.PureComponent {}
UNSORTED
Есть два типа компонентов - функциональные - states - очень простые (тупые), без состояний (presentational, stateless, dumb). Это функция, которая принимает props и возвращает JSX - классовые - с использованием классов ES6 - с состояниями. Можно использовать методы жизненного цикла. Необходимы, если компонент имеет состояние или значимые методы.
React-разработчики стараются минимизировать использование классовых компонент. Если можно решить вопрос функциональной компонентой - так и делай
Есть два типа синтаксиса
-
функциональные компоненты. Для очень простых компонентов, почти без логики (stateless компоненты): import React, {Component} from 'react'; function Article(props) {}
-
классовый компонент (классы компонентов). Позволяет использовать дополнительные возможности, такие как локальное состояние и методы жизненного цикла. Наследуется от базового компонента Component Должны содержать функцию render() Компоненты, основанные на классах, могут хранить информацию о текущей ситуации. Эта информация называется состоянием (state), она хранится в JS-объекте.
import React, {Component} from 'react'; class Article extends PureComponent { render () {} } метод render нужен обязательно, он отвечает за то, как будет выглядеть компонент. props будет жить в this.props
Классовые компоненты
Класс, который наследуется от метода React.Component, у которого есть как минимму метод render, и который возвращает JSX
Для чего нужны классы? Чтоб создавать однотипные объекты на базе этих классов и реализовать в них концепции ООП (инкапсуляция, полиморфизм, наследование) - Полиморфизм - свойство системы использовать объекты с одинаковым интерфейсом без информации о типе и внутренней структуре объекта. - Наследование – это свойство системы, позволяющее описать новый класс на основе уже существующего с частично или полностью заимствующейся функциональностью. Класс, от которого производится наследование, называется базовым или родительским. Новый класс – потомком, наследником или производным классом. - Инкапсуляция – сокрытие деталей. Свойство системы, позволяющее объединить данные и методы, работающие с ними, в классе и скрыть детали реализации от пользователя. - Абстрагирование – это способ выделить набор значимых характеристик объекта, исключая из рассмотрения незначимые. Соответственно, абстракция – это набор всех таких характеристик.
Методы и обработчики классовых компонент писать так (кроме render): onClick = () => {}
https://www.youtube.com/watch?v=vO63wxg4aKY
Позволяет решить вопросы c контекстом вызова (bind и т.д.) -
Не забыть, что все обработчики (практически все) объявляются внутри классовой компоненты, как её метод
setState - метод компонента. Обновляет его состояние, и вызывает перерисовку
Вызов setState позволяет React перестроить ваше приложение и обновить DOM.
Обычно когда необходимо обновить компонент вы просто вызываете setState с новым значением переданным в виде объекта в функцию setState:
this.setState({someField:someValue})
setState - использование стрелочной функции вместо объекта
https://clck.ru/GDfFh
раньше: setState(nextState)
теперь: setState((s,p) => s)
позволяет использовать текущее состояние this.State, не опасаясь, что произойдёт что-то не то.
То есть: позволит получить вам достоверные значения для state и props компонента.
Иначе: так как this.props и this.state могут обновляться асинхронно, то не стоит полагаться на их значения для вычисления нового состояния.
Т.е. я хочу просто инвертировать свойство внутри state (например open/close), и делаю !this.state.isOpen. Но, к моменту выполнения кода этот параметр может уже измениться из другого места, и я получу неожиданный результат
Обновления state могут быть асинхронными
React может сгруппировать несколько вызовов setState() в одно обновление для улучшения производительности.
Поскольку this.props и this.state могут обновляться асинхронно, вы не должны полагаться на их текущее значение для вычисления следующего состояния.
Например, следующий код может не обновить счётчик:
// Неправильно
this.setState({
counter: this.state.counter + this.props.increment,
});
Правильно будет использовать второй вариант вызова setState(), который принимает функцию, а не объект. Эта функция получит предыдущее состояние в качестве первого аргумента и значения пропсов непосредственно во время обновления в качестве второго аргумента:
// Правильно
this.setState((state, props) => ({
counter: state.counter + props.increment
}));
Set State перестраивает весь виртуальный DOM компонента, на котором он вызван.
И всех его вложенных компонентов! А потом вносятся изменения в реальный DOM.
Поэтому - если нет необходимости - не вызывай изменения set state у родителей, чтоб лишний раз не перестраивать виртуальный DOM всех их потомков. Т.е. верхние уровни задействуем только тогда, когда это нужно
- Какой второй аргумент может быть передан в setState? Это функция обратного вызова, вызовется когда элемент отрендерен
Это функция обратного вызова.
Она реализовывается строго после setState, когда элемент отрендерен, и является полностью опциональной.
Рекомендуется отдать предпочтение другому методу, нежели данной функции, но знать о ее существовании и принципе работы не помешает:
this.setState(
{ username: 'tylermcginnis33' },
() => console.log('setState has finished and the component has re-rendered.')
)
Ссылки:
Компоненты контролируемые и не контролируемые (controlled и uncontrolled) //ToDo - доработать
Контролируемые - работают через state, получают и пишут данные обычно в state.
Неконтролируемые - работают напрямую с виртуальным DOM деревом, обычно через ссылку ref.
Дух React делает упор на контролируемых компонентах.
В HTML элементы формы, такие как input, textarea и select, как правило, поддерживают свое собственное состояние и обновляют его на основе пользовательского ввода. Когда пользователь отправляет форму, значения из элементов, упомянутых выше, отправляются вместе с формой.
В React это работает по-другому. Компонент, содержащий форму, будет отслеживать значение ввода в своем состоянии и повторно визуализировать компонент каждый раз, когда вызывается функция обратного вызова onChange, например, при обновлении состояния.
Элемент ввода формы, значение которого контролируется React, таким образом называется «контролируемым компонентом».
Ссылки:
PureComponent //ToDo - доработать
Особый способ создания классовых компонент.
Изменяет lifecycle-метод shouldComponentUpdate, автоматически проверяя, нужно ли заново отрисовывать компонент.
PureComponent будет вызывать функцию render(), только если обнаруживает изменения в props или в состоянии. В некоторых случаях React.PureComponent более эффективен и определенно уменьшает количество кода.
По сути, вариант реализации метода shouldComponentUpdate - поверхностно сравниваются все старые/новые property и все старые/новые state. Если хоть что-то поменялось - перерисовываем компонент
Разница между Component и PureComponent заключается в методе updating lifecycle: shouldComponentUpdate.
Component не реализует shouldComponentUpdate(), PureComponent реализует его поверхностным сравнением пропсов и состояния.
В Component этот метод выглядит так:
shouldComponentUpdate() {
return true;
}
В PureComponent:
shouldComponentUpdate(nextProps, nextState) {
return !shallowEqual(nextProps, this.props) || !shallowEqual(nextState, this.state);
}
Что такое shallowEqual? Это по сути сравнение оператором === каждого элемента из prevProps с каждым элементом из nextProps.
Метод shouldComponentUpdate() базового класса React.PureComponent делает только поверхностное сравнение объектов. Если они содержат сложные структуры данных, это может привести к неправильной работе для более глубоких различий (то есть, различий, не выраженных на поверхности структуры).
Наследуйте класс PureComponent только тогда, когда вы ожидаете использовать простые пропсы и состояние, или используйте forceUpdate(), когда знаете, что вложенные структуры данных изменились. Также подумайте об использовании иммутабельных объектов, чтобы упростить процесс сравнения вложенных данных.
Кроме того, метод shouldComponentUpdate() базового класса React.PureComponent пропускает обновление пропсов для всего поддерева компонентов. Убедитесь, что все дочерние компоненты также являются «чистыми».
Важное замечание: PureComponent нужно использовать только для так называемых presentational components, т.е. для тех компонент, которые НЕ обёрнуты в вызов redux connect().
Для container components (т.е. тех компонент, которые обёрнуты в redux connect()) нет смысла наследоваться от PureComponent, т.к. метод connect() оборачивает ваш компонент своей реализацией shouldComponentUpdate, которая также использует shallowEqual. Если вы по недосмотру унаследуете container component от PureComponent - ошибок не будет, но это не имеет никакого смыла, т.к. ваш код по сути будет дважды делать shallowEqual, а зачем делать лишнюю работу?
Важно помнить, что PureComonent пропускает отрисовку не только самого компонента, но и всех его “детей”, так что безопаснее всего применять его в presentational-компонентах, без “детей” и без зависимости от глобального состояния приложения.
В случае, если pure-компонент имеет детей, все дочерние компоненты, зависящие от смены контекста, не будут реагировать на изменения, если в родительском pure-компоненте не будет объявлен contextTypes.
UNSORTED
Важно: при вызове render() перерисовывается не только родительский компонент, но и все дочерние (хотя, в них свойства могли и не поменяться). Т.е. если у родительского компонента внутри render есть дочерние компоненты - они будут перерисовываться. Соотвественно, если мы вызываем render на родительском компоненте - перерисуем всё приложение. Чтоб решить этот вопрос - используем создание компонента от PureComponent
Даже если я создал класс от PureComponent - это не гарантирует отсутсвие лишних ренедров при тех же данных. Одна из причин - анонимные функци (они при каждом рендере новые).
//Неверно:
<Component onClick= {() => this.hangleClick}>
//Верно:
<Component onClick= {this.hangleClick}>
автоматически проверяет, должен ли компонент обновляться. Не нужно писать shouldComponentUpdate самостоятельно.
PureComponent будет вызывать функцию render(), только если обнаруживает изменения в props или в состоянии. В некоторых случаях React.PureComponent более эффективен и определенно уменьшает количество кода.
Если в props вы передаёте объекты которые иногда мутируются, т.е. по ссылке они равны ===, но внутри какие-то данные поменялись (что само по себе выглядит странно в экосистеме redux + reselect, но вполне возможно технически), тогда использование PureComponent вам всё поломает, т.к. на экране какие-то компоненты перестанут перерисовываться!
Если же у вас всё по уму, данные которые передаются через props являются скалярными типами (string, int, float, bool) или immutable объектами, тогда смело используйте PureComponent - в некоторых случаях он поможет избавиться от лишних вызовов render.
Важное замечание: PureComponent нужно использовать только для так называемых presentational components, т.е. для тех компонент, которые НЕ обёрнуты в вызов redux connect().
Для container components (т.е. тех компонент, которые обёрнуты в redux connect()) нет смысла наследоваться от PureComponent, т.к. метод connect() оборачивает ваш компонент своей реализацией shouldComponentUpdate, которая также использует shallowEqual. Если вы по недосмотру унаследуете container component от PureComponent - ошибок не будет, но это не имеет никакого смыла, т.к. ваш код по сути будет дважды делать shallowEqual, а зачем делать лишнюю работу?
По сути, вариант реализации метода shouldComponentUpdate - поверхностно сравниваются все старые/новые property и все старые/новые state. Если хоть что-то поменялось - перерисовываем компонент
Подводя итог, рецепт такой: - presentational components наследуем от React.PureComponent - container components (которые обёрнуты в redux connect()) наследуем от старого доброго React.Component
Важно помнить, что PureComonent пропускает отрисовку не только самого компонента, но и всех его “детей”, так что безопаснее всего применять его в presentational-компонентах, без “детей” и без зависимости от глобального состояния приложения. В случае, если pure-компонент имеет детей, все дочерние компоненты, зависящие от смены контекста, не будут реагировать на изменения, если в родительском pure-компоненте не будет объявлен contextTypes.
Ссылки
- Оф. документация - React.PureComponent
- habr - Разбор: как и зачем применять PureComponent в React
- PureComponent и Components
- csssr - Основы производительности React-приложений
- YouTube - Какие props портят производительность
- totser
- habr - Оптимизация производительности в React
Компоненты высшего порядка (Higher Order Components, HOC) //ToDo - доработать
Способ повторного использования логики.
Это функция, которая принимает компонент и возвращает новый контейнерный компонент (функциональный или классовый).
Благодаря HOC мы можем создавать одотипные контейнерные компоненты, передававя в них презентационные компоненты (которые надо обернуть)
Если функция возврщает JSX (HTML внтри js-кода) - это комопнента. Если возвращает другую компоненту - это HOC
Примеры:
//Компонента, возвращает JSX
let User = (props) => {
return <div>
<h1>[props.name]</h1>
</div>
}
//Тоже компонента (похоже контейнерная), возвращает JSX (который отрисовывает другую компоненту)
let UserContainer = (props) => {
return <User example="text" />
}
//HOC, принимает комопоненту, возвращает другую компоненту (контейнерную)
let HOComponent = (User) => {
return UserContainer
}
Если точнее, будет так:
//HOC, принимает комопоненту, возвращает другую компоненту (контейнерную)
let HOComponent = (Component) => {
let WrapperContainer = (props) => {
return <Component example="text" />
}
return WrapperContainer
}
Connect из Redux - это тоже HOC. Ну, если быть точным - он возвращает HOC
withRouter из React Router - это тоже HOC.
HOC часто называют с префиксом with - withRedirect, withAuth... В заивисмости от того, какую функциональность добавляет данный HOC.
Ещё есть декораторы. Судя по всему - реализуют похожую функциональность.
Ссылки
- Оф. документация - Компоненты высшего порядка
- YouTube - IT Kamasutra 69
- YouTube - IT Kamasutra 70
- По поводу паттернов в React
- habr - Паттерны React
Render Callbacks (Функция в render) //ToDo - осмыслять
Ещё один способ сделать логику переиспользуемой. Это становится возможным при помощи передаваемого children-а в виде функции.
Ссылки
Советы по организации кода
Компоненты обычно располагаются в папке src/components.
На каждый компонент обычно заводят отдельный файл.
Название файла обычно = названию компонента, также с большой буквы. Например: src/components/Article.js
Каждый раз добавляя новую функциональность - думать чтоб вынести её в отдельный компонент.
Каждый раз дублируя код - думать чтоб вынести её в отдельный компонент/функцию.
Быстродействие. Оптимизация //ToDo - осмыслять
Ссылки
- Гайд по оптимизации веб- приложений в 2017
- habr -
- habr -
- habr -
- habr -
- habr -
- CSSSR - Основы производительности React-приложений
- habr - Оптимизация производительности в React
REACT - компоненты. Жизненный цикл
Методы, которые React вызывает при разных событиях из жизни компонента (появление, удаление...)
Каждый классовый компонент имеет несколько «методов жизненного цикла».
Можно переопределить методы для запуска кода в определённое время в процессе работы приложения.
Единственный обязательный метод в подклассе React.Component — render(). Все остальные методы, описанные ниже, являются необязательными.
Префикс в названии
- will - вызываются прямо перед тем, как что-то происходит,
- did - вызываются сразу после того, как что-то происходит.
- should - должен
1. Монтирование - когда экземпляр компонента создаётся и монтируется в DOM
constructor() - конструктор, в котором происходит начальная инициализация компонента
Если вы не инициализируете состояние и не привязываете методы, вам не нужно реализовывать конструктор для вашего компонента React.
Конструктор компонента React вызывается до его монтирования. При реализации конструктора подкласса React.Component вы должны вызвать super(props) перед любым другим оператором. В противном случае this.props не будет определен в конструкторе, что может привести к ошибкам.
Как правило, в React конструкторы используются для двух целей:
- Инициализация локального состояния путем присвоения объекта this.state.
- Привязка методов-обработчиквов событий к экземпляру.
Вы не должны вызывать setState() в constructor(). Вместо этого, если вашему компоненту нужно использовать локальное состояние, присвойте начальное состояние this.state непосредственно в конструкторе
render() - рендеринг компонента
чистый, не пихать сюда ничего. Только для того, чтобы строить виртуальный DOM компонента
componentDidMount() - после рендеринга компонента. Здесь можно выполнять запросы к удаленным ресурсам.
- реагируем на появление компонента в реальном DOM.
- Например получить размеры, позиционирование, подписаться на изменение данных, повесить свои listener на DOM-элементы...
- происходит один единственный раз - при отрисовке компоненты на странице. При обновлении - не вызывается.
- Все сайд-эффекты делать в ComponentDidMount() Например, там вызываются запросы на сервак, AJAX-запросы, setTimeout и все манипуляции с DOOM
2. Обновление - когда компонент перерисовывается. Может быть вызвано изменениями в state или props
static getDerivedStateFromProps()
Заменяет componentWillReceiveProps()
Вызывается непосредственно перед вызовом метода render, как при начальном монтировании, так и при обновлениях. Должен вернуть объект для обновления состояния или null, чтобы ничего не обновлять.
Этот метод запускается при каждом рендере, независимо от причины.
Метод существует для редких случаев, когда state компонента зависит от изменений в props. Т.е. когда состояние компонента должно меняться, в зависимости от изменений props, который переадёт родитель.
Как и метод render, getDerivedStateFromProps должен быть чистой функцией props и state.
Есть более простые альтернативы, при прочих равных лучше использовать их. Убедитесь, что вы знакомы с простыми альтернативами:
- Чтобы выполнить побочный эффект при изменении пропсов (например, сетевой запрос или анимацию) используйте componentDidUpdate.
- Чтобы повторно вычислить данные при изменении пропсов, используйте функцию мемоизации.
- Чтобы «сбросить» некоторое состояние при изменении пропсов, используйте управляемые компоненты или неуправляемые компоненты с ключом.
В большинстве случаев от применения getDerivedStateFromProps (и его предшественника componentWillReceiveProps) можно избавиться, перемещая управление состоянием в родительский компонент.
Помните, что большинство компонентов не нуждаются в getDerivedStateFromProps. Он не предназначен для использования точь в точь, как componentWillReceiveProps.
Ссылки
- static getDerivedStateFromProps()
- React v16.4.0: события указателя
- YouTube - React 16.3 news (субтитры)
shouldComponentUpdate() - каждый раз при обновлении объекта props или state
- позволяет оптимизировать приложение в ручном режиме, управляя тем, нужно ли перестраивать виртуальный DOM для этого компонента или нет?
- т.е. позволяет оптимизировать перерисовку виртуального DOM - если в это компоненте ничего не поменялось, не перерисовываем
- Вызывается при изменении родителей, и при смене setState в самом компоненте.
- Не забывать, про сравнение ссылочных типов. Не должно быть мутации данных
- Каждый раз, когда меняются данные, должна создаваться новая ссылка. либо заменяем все мутабельные операции на аналогичные иммутабельные операции, либо создаем новую ссылку и затем уже её мутируем.
- Новая ссылка должна создаваться только тогда, когда меняются данные.
- Предупреждает, что сейчас будем перестраивать виртуальный DOM этого компонента. Можно отреагировать на изменения - загрузить текст для статьи, которая открывается и т.д. Вызывается и при изменении родителей, и при смене setState в самом компоненте.
Ссылки: YouTube - Оптимизация приложений, shouldComponentUpdate
render()
- чистый, не пихать сюда ничего. Только для того, чтобы строить виртуальный DOM компонента
- Использовать его для обновления компонента. Можно, при создании компонента, наследовать его от компонента PureComponent - тогда сравниваются ВСЕ props (старые и новые) и ВСЕ элементы state (старые и новые). Т.е. не надо каждый раз объявлять «отслеживай ещё изменения этого параметра» — он будет следить по-умолчанию. Но, без необходимости лучше не использовать — могу вылезти сложные баги
componentDidUpdate() - сразу после обновления компонента (если shouldComponentUpdate возвращает true
чаще всего нужен, если нас интересуют составляющие реального DOM (размер компонента, позиционирование...)
3 Демонтирование - когда компонент удаляется из DOM
componentWillUnmount() - перед удалением компонента из DOM.
предупреждает что компонент будет удалён.
Подчищаем подписки, проводим логику деструктуризации компонента
Например, остановить и обнулить таймер
4 Обработка ошибок - при возникновении ошибки
componentDidCatch()
Вызывается при ошибках:
- ошибки во время отрисовки
- ошибки в методе жизненного цикла
- ошибки в конструкторе любого дочернего компонента.
Эти методы считаются устаревшими. Избегайте их:
1. componentWillMount() - непосредственно перед рендерингом компонента
Часто используется для получения данных (отправка запроса за статьёй на сервер и т.д.)
Вместо него лучше использовать componentDid Mount
На самом деле:
- если нужно выставить изначальное состояние компоеннта - делайте это в конструкторе
- если нужно изменять DOM - делайте это в componentDid Mount
2. componentWillReceiveProps()
При обновлении props, до монтирования новых.
Вместо него добавляется новый - getDerivedStateFromProps
Здесь надо обновлять state компонента из приходящих props
Тут приходят новые props.
Вызывается до монтирования новых пропсов в компонент.
Обычно в этой функции устанавливаются свойства компонента (в том числе из this.state), которые зависят от значений из пришедших в компонент props
React передаёт новые props, которые можно сравнить с текущими. Вызывается только если поменялся кто-то из родителей и буду меняться какие-то props.
Два основных варианта использования:
- поменялись важные данные и надо отреагировать. Например пришла новая статья и надо загрузить её с сервера
- мы завязали состояние компонента на porps (не лучшая практика, но иногда оптимальная) - теперь надо следить за изменениями в props, и приводить state к нужному виду. Например, состояние статьи (свёрнута/развёрнута) приходят в компонент из другого компонента, через props. Тогда надо отслеживать -изменились ли эти props, и перерисовывать состояния статьи
2. componentWillUpdate()
перед обновлением компонента (если shouldComponentUpdate вернул true)
Вместо него рекомендуется использовать componentDidUpdate
I. Другие API - каждый компонент также предоставляет некоторые другие API:
- setState()
- forceUpdate()
II. Свойства класса
- defaultProps
- displayName
III. Свойства экземпляра
- props
- state
Unsorted
- Сначала определяется шаблон React.js для создания элементов из компонента.
- Указывается где он будет использован. К примеру, внутри вызова функции рендера иного компонента или с помощью ReactDOM.render.
- Реакт создает экземпляр элемента и передает ему набор свойств (props), доступ к которым будет доступен через this.props.
- Поскольку описанное является JavaScript-ом, будет вызван метод конструктора класса (если он определен). Это первый из методов, которые называются методами жизненного цикла компонента.
- React обрабатывает результат вызова функции рендера.
- Затем React осуществит монтирование компонента: взаимодействуя с браузером через DOM API, React выполнит рендеринг.
- Следом, Реакт вызывает другой метод жизненного цикла, который называется componentDidMount. Этот метод можно использовать, чтобы что-то сделать в дереве документа. Весь DOM, с которым мы работали ранее был виртуальным.
- Демонтирование. Жизненный цикл некоторых компонентов заканчивается уже на этом этапе. Компоненты могут быть демонтированы из документа по разным причинам. Однако, перед этим Реакт вызывает другой метод – componentWillUnmount.
Реакт контролирует состояние каждого компонента на случай изменений. Для того, чтобы React действовал эффективно, необходимо изменить поле состояния с помощью API React и функции this.setState.
Входными данными для render() являются свойства (props) и внутреннее состояние, которое может быть обновлено в любое время.
Когда для render меняются входные данные, меняется и результат ее выполнения.
React.js ведет запись жизненного цикла компонента. Когда React.js видит, что один рендер отличается от другого, он переводит разницу между своим виртуальным представлением в операции с DOM API, которые будут отрисованы в документе.
Теперь, когда мы знаем, что за магия происходит при изменении состояния компонента, рассмотрим оставшиеся концепции.
- Компонент может быть необходимо повторно отрисовать, если его состояние будет обновлено, либо, если родительский элемент изменит свои свойства.
- Если были изменены свойства, React.js вызовет метод жизненного цикла componentWillReceiveProps.
- Если объект или его свойства были изменены, React.js вызывает еще один метод – shouldComponentUpdate, который, по сути, является вопросом. Так что, если есть необходимость самостоятельно настроить процесс рендера, вы можете ответить на этот вопрос вернув true или false.
- Если shouldComponentUpdate не объявлен, Реакт вызовет безусловный componentWillUpdate и рассчитает различия между текущим компонентом и его новым видом, с учетом изменений.
- Если никаких изменений не зафиксировано, React.js ничего не сделает.
- Если разница есть, фреймворк отрисует компонент.
- Так как процесс обновления в любом случае произошел, Реакт вызовет метод componentDidUpdate.
- Сначала мы определяем шаблон для React для создания элементов из компонента.
- Затем мы указываем React где будем его использовать. Например, внутри вызова функции render другого компонента или с помощью ReactDOM.render.
- Затем React создает экземпляр элемента и передает ему набор props, к которым мы можем получить доступ с помощью this.props. Эти props — это то, что мы передали на шаге 2.
- Поскольку это все JavaScript, то будет вызван метод конструктора (если он определен). Это первый метод из тех, что мы называем методами жизненного цикла компонентов.
- Затем React обрабатывает результат вызова функции render (получает виртуальный узел DOM).
- Поскольку это первый раз, когда React выполняет рендеринг элемента, React будет взаимодействовать с браузером (от нашего имени, используя DOM API), чтобы отобразить в нем элемент. Этот процесс широко известен как монтирование.
- Затем React вызывает другой метод жизненного цикла, называемый componentDidMount. Мы можем использовать этот метод, чтобы, например, сделать что-то в DOM, который, как мы знаем, существует в браузере. До этого метода жизненного цикла, DOM, с которым мы работали, был виртуальным.
- Некоторые истории компонентов заканчиваются здесь. Компоненты демонтируются из DOM браузера по разным причинам. Но перед тем как это произойдет, React вызывает другой метод жизненного цикла, componentWillUnmount.
- Состояние любого смонтированного элемента может измениться. Родитель этого элемента может быть повторно отрисован. Также смонтированный элемент может получить другой набор props. И здесь начинается магия React и наступает именно тот момент, когда React нам так необходим! Честно говоря, до этого он нам особо и не был нужен.
- История этого компонента не заканчивается, но прежде чем продолжить, нам нужно понять, что же это за состояние, о котором я говорю.
Ссылки
REACT - Context API
Что такое контекст и зачем он нужен?
Объект, который создаётся у родителя и доступен всем детям.
Контекст предоставляет способ делиться данными между компонентами без необходимости явно передавать пропсы через каждый уровень дерева.
Может содержать некоторые очень глобальные данные - активный язык приложения (ru/en), активную тему оформления (ночь/день), store... Т.е. что-то, что редко меняется
Что попало туда пихать не надо, только очень глобальные вещи.
Контекст нужен чтобы не пробрасывать некоторые данные по длинной цепочке только для того, чтоб они пришли в компоненту нижнего уровня - эти данные помещаются в контекст а компонента нижнего уровня берёт их сразу оттуда, не из props. Обычно контекст используется, если необходимо обеспечить доступ данных во многих компонентах на разных уровнях вложенности. По возможности не используйте его, так как это усложняет переиспользование компонентов.
Раньше был старый синтаксис Context, его использовать не надо
Context API: createContext, Provider, Consumer
- Функция React.createContext, которая создает объект Context
- Provider (возвращается createContext) - устанавливает «шину» для прямой передачи данных, проходящую через дерево компонентов
- Consumer (возвращается createContext) - впитывается в «шину» для извлечения данных
Provider
Provider очень похож на Provider в React-Redux. Он принимает значение, которое может быть всем, чем хотите (это может быть даже store Redux… но это было бы глупо). Скорее всего, это объект, содержащий ваши данные и любые actions, которые вы хотите выполнить с данными.
Consumer
Consumer работает немного похоже как функция connect в React-Redux, подключаясь к данным, и сделав их доступными для компонента, который их использует.
Ссылки
REACT - Хуки
Что такое Хук и зачем он нужен?
Хук — функция, с помощью которой можно «подцепиться» к state и методам жизненного цикла React из функц. компонент. Не работает в классах.
Хуки — это способ использовать повторно логику состояния, а не само состояние.
Каждое обращение к хуку обеспечивает совершенно изолированное состояние. Вы даже можете использовать один и тот же хук несколько раз в одном компоненте.
React содержит несколько встроенных хуков, таких как useState. Можно создавать собственные хуки, чтобы повторно использовать их в других компонентах.
Хуки следует вызывать только на верхнем уровне. Не вызывайте хуки внутри циклов, условий или вложенных функций.
Хуки следует вызывать только из функциональных компонентов React. Не вызывайте хуки из обычных JavaScript-функций.
Есть только одно исключение, откуда можно вызывать хуки — это ваши пользовательские хуки.
Поскольку хуки являются функциями, мы можем передавать информацию между ними.
Ссылки
Пользовательские хуки
функция, имя которой начинается с «use», и которая может вызывать другие хуки.
Ссылки
Пример хука состояния (useState)
import React, { useState } from 'react';
function Example() {
// Объявление новой переменной состояния «count»
const [count, setCount] = useState(0);
return (
<div>
<p>Вы кликнули {count} раз(а)</p>
<button onClick={() => setCount(count + 1)}>
Нажми на меня
</button>
</div>
);
}
Вызов useState вернёт пару значений: текущее состояние и функцию, обновляющую состояние. Поэтому мы пишем const [count, setCount] = useState(). Это похоже на this.state.count и this.setState в классах, с той лишь разницей, что сейчас мы принимаем их сразу в паре.
В классовой компоненте это выглядело бы так:
class Example extends React.Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<div>
<p>Вы кликнули {this.state.count} раз(а)</p>
<button onClick={() => this.setState({ count: this.state.count + 1 })}>
Нажми на меня
</button>
</div>
);
}
}
Чтоб прочитать state из хука мы используем переменную, которую ранее объявили Например:
Вы кликнули {count} раз(а)
В классовой компоненте это было бы так:
Вы кликнули {this.state.count} раз(а)
Чтобы обновить state: <button onClick={() => setCount(count + 1)}> Нажми на меня
В классовой компоненте это было бы так: <button onClick={() => this.setState({ count: this.state.count + 1 })}> Нажми на меня
В отличие от this.setState в классах, обновление переменной состояния всегда замещает её значение, а не осуществляет слияние.
Ссылки
REACT - Роутинг
Что такое React-Router?
React-Router - набор компонентов определяющих на основе текущего пути, какой компонент будет выводиться.
- react-router - пакет с базовым набором функций
- router-dom - пакет с набором функций для работы в браузере
BrowserRouter и HashRouter
Для браузерных проектов есть BrowserRouter и HashRouter компоненты.
- BrowserRouter — следует использовать когда вы обрабатываете на сервере динамические запросы. Если проект предполагает бекенд - бери BrowserRouter.
- HashRouter - когда у вас статический веб сайт.
Обычно предпочтительнее использовать BrowserRouter, но если ваш сайт расположен на статическом сервере(как github pages), то использовать HashRouter это хорошее решение проблемы.
Компонент Route
- компонент, строительный блок React Router'а. Если вам нужно рендерить элемент в зависимости от pathname URL'ов, то следует использовать компонент
Как связаны Route и NavLink?
Route и NavLink = два независимых элемента.
Фактически, это обычные компоненты, написанные разработчиками, и подключаемые из билиотеки. В них передаются параметры и функции. ПРи наступлении условия X сделай то-=то (например, отрисуй компоненту такую-то)
- Route - меняет содержимое страницы, в зависимости от того, что введено в адресной строке. Следит за ней, и при изменениии - отрабатывает
- NavLink - при клике меняет адрес в адресной строке
Ссылки
REACT - Статическая типзация
Про статическую типизацию
При статической типизации мне необязательно напоминать компилятору, что данная переменная, например, целое число, и всегда должно им оставаться. Эта информация хранится в программе, и даже если я, забывшись, попытаюсь изменить ее значение на недопустимое, ничего страшного не произойдет.
Статическая типизация: Проверка типов на стадии компиляции, перед запуском программы. C, C++, C#, Java, Pascal...
Динамическая типизация: Проверка типов когда программа уже запущена. Perl, Ruby, JavaScript, Lisp, PHP, Python...
Статические языки производительнее.
Статические языки - с помощью тестирования типов легко проверить работоспособность кода еще до его выполнения.
Эффективность. Статическая типизация требует от программиста большей ответственности. Динамические языки, в свою очередь, могут поощрять некоторую раскованность, вырабатывая у разработчика привычку следовать дурным паттернам.
В каждом случае если вы хотите использовать типы, то явно говорите инструменту, в каких файлах осуществлять проверку типов.
-
В случае TypeScript вы делаете, создавая файлы с расширением .ts вместо .js.
-
В случае Flow вы указываете в начале кода комментарий @flow.
-
Динамический пример на JS var name = "Susan", age = 25, hasCode = true;
-
Статический пример на TypeScript let name: string = "Susan", age: number = 25, hasCode: boolean = true;
Ссылки
- Оф. документация - Статическая типизация
- Hexlet - Введение в программирование. Типизация
- Wat by Gary Bernhardt (юмористическое видео про неявную типизацию)
Type Script - язык (Microsoft)
язык разработанный Microsoft. Совместим с JS (расширяет его). Добавляет возможности статической типизации и ООП.
представляет собой надмножество, которое компилируется в JavaScript — хотя по ощущениям TypeScript похож на новый язык со статической типизацией сам по себе. То есть очень похож на JavaScript и не сложен в освоении.
есть аналогичное решение от Facebook - Flow.
в Angular разработка ведётся на TypScript
Динамический пример на JS
var name = "Susan",
age = 25,
hasCode = true;
Статический пример на TypeScript
let name: string = "Susan",
age: number = 25,
hasCode: boolean = true;
Надо явно сообщить React, в каких файлах осуществлять проверку типов - создаём файлы с расширением .ts вместо .js. Уточнить: кажется, это вообще отдельные от кода файлы с описанием типов.
Ссылки
Flow - библиотека (Facebook)
open source библиотека для статической проверки типов, которую разработала и выпустила Facebook. Позволяет постепенно добавлять типы в ваш код JavaScript. Flow представляет собой инструмент статического анализа, который использует надмножество языка, позволяя добавлять аннотации типов к ко всему вашему коду и улавливать весь класс ошибок во время компиляции.Надо явно сообщить React, в каких файлах осуществлять проверку типов - указывать в начале кода комментарий @flow.
PropTypes - библиотека React
Очень простой - проверяет только props.
- изначально был в составе React, потом вынесли в отдельную библиотеку
- функциональность намного меньше и проще, чем у TS/Flow
- проверяет только props
- выдаёт предупреждения об ошибках типов во время запуска
PropTypes может давать вам предупреждения во время выполнения, что может быть полезно для быстрого поиска неверных ответов, поступающих с сервера, и т.
В существующих приложениях с большими объектами, это быстро приведет к большому количеству кода. Это проблема, так как в Реакте часто нужно передавать один и тот же объект множеству компонентов. Повторение этого процесса во множестве компонентов нарушает принцип DRY (Don’t Repeat Yourself). Самоповторы приводят к проблемам с поддержкой.
Проблема с использованием PropTypes вместе с Flow заключается в том, что вы пишете много дубликатов кода. Оба определения в основном содержат ту же информацию, и когда тип данных изменяется, необходимо обновить оба определения.
- propTypes - специальное свойство класса компонента (так было раньше?). Задают типы входных параметров для отрисовываемого компонента.
В случае несовпадения (например вместо числа пришла строка) позволяют получить ошибку в react-dev-tools и отловить этот момент во время выполнения.
Это была встроенная возможность контроля типов для больших приложений, но с недавнего времени вынесена в отдельный пакет.
Для некоторых приложений, вы можете использовать расширения JavaScript такие как Flow или TypeScript осуществляя проверку типов всего вашего приложения. Но если вы не используете таковые — React предоставляет некоторые встроенные возможности проверки типов.
Пример:
class Greeting extends React.Component {
render() {
return (
<h1>Hello, {this.props.name}</h1>
);
}
}
Greeting.propTypes = {
name: React.PropTypes.string
};
В целях производительности, propTypes проверяются только в режиме разработки (development). Т.е. сам код остаётся, но не проверяется. Для удаления кода есть спец. модуль
propTypes и defaultProps это статичные свойства. Объявлять их надо как можно выше в коде (позже вам скажут спасибо многие разработчики).
Различия PropTypes и Flow
Flow - почти язык
PropTypes - едва ли библиотека
Кроме того, что и PropTypes и Flow относятся к очень широкому полю проверки типов, между ними нет особого сходства.
Flow представляет собой инструмент статического анализа, который использует надмножество языка, позволяя добавлять аннотации типов к ко всему вашему коду и улавливать весь класс ошибок во время компиляции.
PropTypes - это базовая проверяльщик типов, который был частью React. Он не может проверять ничего, кроме типов props, передаваемого данному компоненту.
Если вам нужен более гибкий метод проверки типов для всего проекта, то Flow/TypeScript являются подходящими.
Пока вы передаете только аннотированные типы в компоненты, вам не понадобятся PropTypes.
Если вы просто хотите проверить типы props, не делайте излишнюю усложнение остальной части своей кодовой базы и идите с более простой опцией.
- Flow - инструмент статического анализа
- PropTypes - инструмент проверки во время запуска. Теоретически может отловить ошибки, которые могут быть пропущены Flow
Ссылки:
REACT - JSX
Что такое JSX?
JavaScript XML (JSX) - расширение синтаксиса JavaScript, "синтаксический сахар" для JS. Позволяет использовать похожий на HTML синтаксис для описания структуры интерфейса. https://learn.javascript.ru/screencast/react#03-jsx При помощи Babel он компилируется в обычный JS. В JSX пишется и html-содержимое компонентов. Расширение .jsx использоватьтолько для компонент. Не использовать для редьюсеров и т.д.т
- JSX - синтаксическое расширение JavaScript.
JSX производит React-элементы.
Можно работать с React на обычном JS, без JSX.
Babel компилирует JSX в вызовы React.createElement().
Зачем нужен JSX?
React учитывает тот факт, что логика отрисовки связана с другой логикой пользовательского интерфейса: как обрабатываются события, как изменяется состояние со временем и как данные подготавливаются для отображения.
Вместо того, чтобы искусственно отделять технологии, помещая разметку и логику в отдельные файлы, React разделяет задачи , используя слабо связанные единицы, называемыми «компонентами», которые содержат и разметку, и логику.
В JSX нельзя вывести два html-элемента рядом, вот так:
function Test() {
return (
<h1>Title</h1>
<p>Text</p>
<div></div>
)
}
Надо так:
function Test() {
return (
<div>
<h1>Title</h1>
<p>Text</p>
</div>
)
}
Или так:
function Test() {
return [
<h1 key = 'a'>Title</h1>,
<p key = 'b'>Text</p>
]
}
- чтоб создать в JSX пустую корневую компоненту можно сделать так:
return <>
<ComponentOne />
<ComponentTwo />
</>
Иначе - только через массив с уникальными ключами
В JSX обязательно надо закрывать открытый тэг. Но, можно использовать такой синтаксис
В JSX есть соглашение - все кастомные (т.е. мной созданные) компоненты называются с большой буквы .
Пример: Aricle, MyComponent... Т.к. компонент = класс. И при выводе их внутри других компонентов - тоже (, , ...)В JXS, если надо написать кусок на обычном JS, я помещаю его в фигурные скобки.
Например, создаю и вывожу переменную function Test() { const text =Text
return ( ) } Лучше не злоупотреблять выводом внутри JSX фигруных скобок с JS - тяжело разбираться.
Если нужны большие объёмы - выноси в переменные (см выше)
Внутри JSX можно использовать только выражения
Внутри JSX можно использовать только выражения. Так, например, вы не можете использовать оператор if, но можете использовать тернарное выражение.
Переменные JavaScript также являются выражениями
Объекты JavaScript также являются выражениями.
Вы можете использовать элемент React внутри JSX, потому что это тоже выражение
Вы также можете использовать все функциональные методы JavaScript для коллекций (map, reduce, filter, concat и т. д.) внутри JSX. Опять же, потому что они возвращают выражения
JSX предотвращает атаки, основанные на инъекции кода.
https://ru.reactjs.org/docs/introducing-jsx.html Данные, введённые пользователем, можно безопасно использовать в JSX. По умолчанию React DOM экранирует все значения, включённые в JSX перед тем как отрендерить их. Это гарантирует, что вы никогда не внедрите чего-либо, что не было явно написано в вашем приложении. Всё преобразуется в строчки, перед тем как быть отрендеренным. Это помогает предотвращать атаки межсайтовым скриптингом (XSS).Все атрибуты элементов React именуются с помощью camelCase.
CSS-class записываем как className tabindex = tabIndexВнутри JSX разметки можно использовать только готовые выражения.
Нельзя, например, использовать конструкцию if/else (точно?) но можно заменить ее тернарным оператором.REACT - best practices
Ссылки
- Паттерны React
- Гайд как писать на React в 2017
- 11 советов для тех, кто использует Redux при разработке React-приложений
- 9 принципов, которые должен знать новичок в React.js
- Почему не надо сохранять props в state
- Как не надо писать React: неправильные шаблоны и проблемы в React
REACT - CSS
Структура проекта
Если нужно добавить для компонента CSS - создаю для этого компонента отдельную папку (название = названию компонента), в ней файл компонента (index.js) и style.css
Есть ещё различные варианты CSS-in-JS - когда CSS хранится и генерируется прямо в JS-коде. Подходы интересные, но со совимим минусами. См Styled component.
Заметки
- Если не используешь CSS-modules - CSS лучше писать по BEM-методологии - http://ru.bem.info/methodology
- CSS-переменные - https://developer.mozilla.org/ru/docs/Web/CSS/Using_CSS_custom_properties
Сss-modules
CSS модуль — это CSS файл, в котором все имена классов и анимаций имеют локальную область видимости по умолчанию.
Такой метод подключения CSS, при можно использовать одинаковые имена css-классов в разных компонентах, и конфликта не будет. Что-то типа автоматизированного BEM. При это css по-прежнему хранятся в отдельном файле и пишутся практически как обычно
Ссылки
- habr - Практическое руководство по использованию CSS Modules в React приложениях
- habr - Эволюция CSS: от CSS, SASS, BEM и CSS–модулей до styled-components
- You-Tube - IT-Kamasutra 14. Уроки React JS (css-модули, css-modules)
Styled components //ToDo - дополнить
Среди причин выбора CSS-in-JS можно назвать то, что эта технология позволяет ограничивать область видимости стилей и отказаться от глобальной стилизации. Её удобно применять для работы с темами приложений.
В старой версии приложения я использовал библиотеку styled-components. Чем это плохо? Дело в том, что обычный CSS быстрее и занимает меньше места. Современные браузеры умеют загружать CSS-код параллельно с JavaScript-бандлом. Кроме того, для использования обычного CSS не нужно дополнительной библиотеки. Минифицированный вариант styled-components занимает порядка 54 Кб. Использование обычного CSS вместо styled-components привело к тому, что код приложения быстрее загружается, и к тому, что при изменении стилей системе приходится выполнять меньше вычислений
Ссылки
- Официальный сайт проекта (en)
- YouTube - Артём Арутюнян с докладом о библиотеке styled-components
- habr - Знакомство с Styled components
- habr - Эволюция CSS: от CSS, SASS, BEM и CSS–модулей до styled-components
- habr - CSS-in-JS — мифы и реальность (на примере styled-components)
- habr - Анонс новой версии Styled Components v5
- habr - История четырёхкратного ускорения React-приложения
- Medium - Styled-components getting started (en)
- Medium - How to use styled components with Material UI in a React app (en)
REDUX
Redux
Библиотека для управления state, которая реализует Flux-архитектуру
Её надо инсталлировать отдельно
Позволяет создавать свой store командой, а не вручную
В частности, уменьшает связность - позволяет передавать данные не по цепочке props, а сразу в нужную компоненту
Есть несколько библиотек, которые очень хорошо дополняют Redux:
- Immutable.js — немутабельные структуры данных для JavaScript! Используйте их для хранения состояния, чтобы быть уверенным, что оно не меняется там, где не должно, а также чтобы сохранить функциональную чистоту редьюсеров
- redux-thunk — используется когда нужно, чтобы действия (actions) имели какой-либо побочный эффект в дополнение к обновлению состояния приложения. Например, вызов REST API, или установка маршрутов (routes), или даже вызов других действий.
- reselect — используется для создания составных, лениво исполняемых отображений. Например для конкретного компонента вам может потребоваться:
- вставить только определенную часть глобального состояния, а не полностью
- вставить дополнительные производные данные, например "итого" или "результаты валидации данных", не сохраняя все это в состоянии
Ссылки
- Изучаем Redux на примере создания мини-Redux
- Создаем свой собственный Redux, часть 2: функция connect
- Build Yourself a Redux (en)
React-redux
Отдельная библиотека, выступает как прослойка между React и Redux.
Позволяет работать с Redux не заморачиваясь кучей сложностей.
Инкапсулирует часть вещей, прячет от нас всякие детали связанные с контекстом, store, dispatch, subscribe...
Пакет react-redux обладает очень простым интерфейсом. В частности, самое интересное в этом интерфейсе сводится к следующему:
- — позволяет создавать обёртку для React-приложения и делать состояние Redux доступным для всех компонентов-контейнеров в его иерархии.
- connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options]) — позволяет создавать компоненты высшего порядка. Это нужно для создания компонентов-контейнеров на основе базовых компонентов React.
Ссылки
- Оф. документация React-redux (en)
- Оф. документация React - State и жизненный цикл
- Redux и Thunk вместе с React. Руководство для чайников.
State (состояние)
специальный js-объект <внутри компонента>. Хранит данные, которые могут изменятся с течением времени.
Это инструмент, позволяющий обновлять пользовательский интерфейс, основываясь на событиях.
Задачи компоненты - отрисовывать какие-то данные. Эти данные всегда называются state (состояние приложения).
Узнать состояние компонента можно с помощью конструкции this.state.
Изменить состояние можно с помощью this.setState(), если передадим этой функции объект, представляющий новое состояние.
-
state-managment - управление данными
Задумывая архитектуру нового приложения, первым делом всегда думать - как я собираюсь организовать state-managment (управление данными)? Чаще всего выбор зависит от того, в чём больше опыта. State (Business Logic Layer) важнее чем UI. Какие есть подходы к state-managment? - local state of class component - локальный state классовых компонент. Используется не всегда. Для простых, небольших задач, маленьких проектов. - Redux (одна из реализаций FLUX) - функциональное програмирование - MobX - ООП - и ещё много других -
Не изменяйте state напрямую
// Неправильно this.state.comment = 'Привет';Вместо этого используйте setState(): // Правильно this.setState({comment: 'Привет'});
Конструктор — это единственное место, где вы можете присвоить значение this.state напрямую.
-
Обновления state могут быть асинхронными
React может сгруппировать несколько вызовов setState() в одно обновление для улучшения производительности.Поскольку this.props и this.state могут обновляться асинхронно, вы не должны полагаться на их текущее значение для вычисления следующего состояния.
Например, следующий код может не обновить счётчик:
// Неправильно this.setState({ counter: this.state.counter + this.props.increment, });
Правильно будет использовать второй вариант вызова setState(), который принимает функцию, а не объект. Эта функция получит предыдущее состояние в качестве первого аргумента и значения пропсов непосредственно во время обновления в качестве второго аргумента:
// Правильно this.setState((state, props) => ({ counter: state.counter + props.increment }));
-
Однонаправленный поток данных
В иерархии компонентов, ни родительский, ни дочерние компоненты не знают, задано ли состояние другого компонента. Также не важно, как был создан определённый компонент — с помощью функции или класса. Состояние часто называют «локальным», «внутренним» или инкапсулированным. Оно доступно только для самого компонента и скрыто от других. Компонент может передать своё состояние вниз по дереву в виде пропсов дочерних компонентов:Своё состояние можно передать и другому пользовательскому компоненту:
Компонент FormattedDate получает date через пропсы, но он не знает, откуда они взялись изначально — из состояния Clock, пропсов Clock или просто JavaScript-выражения:
function FormattedDate(props) { return
; }Этот процесс называется «нисходящим» («top-down») или «однонаправленным» («unidirectional») потоком данных. Состояние всегда принадлежит определённому компоненту, а любые производные этого состояния могут влиять только на компоненты, находящиеся «ниже» в дереве компонентов.
Если представить иерархию компонентов как водопад пропсов, то состояние каждого компонента похоже на дополнительный источник, который сливается с водопадом в произвольной точке, но также течёт вниз.
Ссылки
Store (хранилище)
ООП-объект, который управляет state (объект хранящий состояние приложения)
Там лежит: - сам state - методы для работы с ним
Создаётся при помощи метода(?) createStore(reducers)
Dispath (отправка)
метод объекта store, через который вызываем все другие методы объекта Store (что изменить state, т.е. состояние приложения)
Мы вводим в наш объект store один единственный метод, через который будем вызывать все другие методы объекта.
Он принимает некий объект action. Выглядит так: dispath(action).
У action обязательно должно быть текстовое свойство type='' - в нём передаётся название требуемого действия (т.е. метода).
Эти текстовые названия всегда пишутся заглавными.
State всегда меняется через dispath(action)
Action
- объект, который через метод dispath передаётся в наш объект store, и там производит некие действия с данными (state)
У action есть как минимум одно свойство, type. По type dispath определяет, какие именно действия надо произвести со state (какую ветку действий выбрать)
Action Creator
функции, которые возвращают объект action. То, что передаётся в mapDispatchToProps
Содержит action - type и список данных, которые может получать.
Пример: export const updateTaskStatus = (status, id) => ({ type: UPDATE_TASK_STATUS, newStatus: status, taskId: id, });
Reducer (уменьшатель)
чистая функция, принимает state и action. Та самая простынь, где много switch
Применяет action к этому state (если нужно) и возвращает новый state (если не изменился - тот же).
Позволяет разделить метод dispath на отдельные куски, чтоб с ним было удобнее работать.
Обычно каждый reducer отвечает за какую-то ветку state - например этот работает с одной страницей, а тот с другой; или один работает с цитатами, а другой с пользователями.
Reducers - это отдельные функции, а не методы объекта store. Они лежат отдельно от store. Поэтому store (и его метод dispatch) не в курсе, какой action какому reducer нужен - мы отправляем любой входящий action всем имеющимся reducers. Для работы reducer ему кроме action нужен ещё и state. Но, мы не отправляем весь state целиком каждому reducer - нет, каждому мы отправляем только ту ветку, с которой он работает.
Reducer - только преобразователь. Он не вызывает subscriber и другие callbacks.
Если тип action неизвестен - выдаём изначальный state
const task_tables_reducer = (state = initialState, action) => { switch(action.type) { case IS_PAGINATION: { return {...state}; } default: return state; } }
connect(mapStateToProps, mapDispatchToProps)
- API-функция предоставляемая пакетом react-redux.
Позволяет создавать контейнерные компоненты
Пробрасывает в презентационную компоненту данные из store, в виде props. Благодаря mapStateToProps
Подписывает презентационную компоненту на все изменения state, которые мы объявили в mapStateToProps
Позволяет контейнерной компоненте изменять store, благодаря mapDispatchToProps
Connect автоматически делает подключенные компоненты «чистыми», то есть они будут повторно рендериться только при изменении их props — тоесть, когда изменяется их срез состояния Redux. Это предотвращает ненужный ре-рендер и ускоряет работу приложения.
Connect() используется для создания компонентов-контейнеров, которые подключены к хранилищу Redux. Хранилище, к которому осуществляется подключение, получают от самого верхнего предка компонента с использованием механизма контекста React.
Если вам, в React-компоненте, нужно получать данные из хранилища, или требуется диспетчеризовать действия, или нужно делать и то и другое, вы можете преобразовать обычный компонент в компонент-контейнер, обернув его в компонент высшего порядка, возвращаемый функцией connect() из react-redux.
Вы можете создать компонент-контейнер самостоятельно и вручную подписать компонент на хранилище Redux, используя команду store.subscribe(). Однако использование функции connect() означает применение некоторых улучшений и оптимизаций производительности, которые, вы, возможно, не сможете задействовать при использовании других механизмов.
Функция connect(), кроме того, даёт разработчику дополнительную гибкость, позволяя настраивать компоненты-контейнеры на получение динамических свойств, основываясь на свойствах, первоначально им переданных. Это оказывается очень кстати для получения выборок из состояния, основываясь на свойствах, или для привязки генераторов действий к конкретной переменной из свойств.
Если ваше React-приложение использует несколько хранилищ Redux, то connect() позволяет легко указывать конкретное хранилище, к которому должен быть подключён компонент-контейнер.
Прежде чем преобразовывать обычный компонент React в компонент-контейнер с использованием connect(), нужно создать хранилище Redux, к которому будет подключён этот компонент.
Функция connect(), предоставляемая пакетом react-redux, может принимать до четырёх аргументов, каждый из которых является необязательным. После вызова функции connect() возвращается компонент высшего порядка, который можно использовать для оборачивания любого компонента React.
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
mergeProps
Если функции connect() передаётся аргумент mergeProps, то он представляет собой функцию, которая принимает следующие три параметра:
- stateProps — объект свойств, возвращённый из вызова mapStateToProps().
- dispatchProps — объект свойств с генераторами действий из mapDispatchToProps().
- ownProps — исходные свойства, полученные компонентом.
Эта функция возвращает простой объект со свойствами, который будет передан заключённому в обёртку компоненту. Это полезно для осуществления условного маппинга части состояния хранилища Redux или генераторов действий на основе свойств.
Если connect() не передают эту функцию, то используется её стандартная реализация:
const mergeProps = (stateProps, dispatchProps, ownProps) => {
return Object.assign({}, ownProps, stateProps, dispatchProps)
}
options
Объект с параметрами. Необязательный объект, передаваемый функции connect() в качестве четвёртого аргумента.
Содержит параметры, предназначенные для изменения поведения этой функции. Так, connect() представляет собой специальную реализации функции connectAdvanced(), она принимает большинство параметров, доступных connectAdvanced(), а также некоторые дополнительные параметры.
Страница документации- какие параметры можно использовать с connect(), и как они модифицируют поведение этой функции.
Ссылки
- Использование функции connect() из пакета react-redux
- Оф. документация React-redux (en)
- Создаем свой собственный Redux, часть 2: функция connect
mapStateToProps
mapStateToProps - функция, которая возвращает либо обычный объект, либо другую функцию.
Передача mapStateToProps в качестве аргумента для функции connect() приводит к подписке компонента-контейнера на обновления Redux Store. mapStateToProps будет вызываться каждый раз, когда состояние Store изменяется. Если слежение за обновлениями состояния не нужно - передайте connect() в качестве значения этого аргумента undefined или null.
mapStateToProps объявляется с двумя параметрами, второй из которых является необязательным.
- Первый параметр представляет собой текущее состояние хранилища Redux.
- Второй параметр (ownProps), если его передают, представляет собой объект свойств, переданных компоненту //ToDo: разбираться
const mapStateToProps = (state, ownProps) => ({
coin: coinSelector(state, ownProps),
isLoading: isCoinsLoadingSelector(state),
});
ownProps
Это свойства компонента.
Как уже было сказано, функции mapStateToProps и mapDispatchToProps, переданные connect(), могут быть объявлены со вторым параметром ownProps, представляющим собой свойства компонента.
Однако тут есть одна проблема. Если число обязательных параметров объявленной функции mapStateToProps меньше, чем 2, тогда ownProps передаваться не будет. Но если функция объявлена с отсутствием обязательных параметров или, как минимум, с 2 параметрами, ownProps будет передаваться.
Если из mapStateToProps будет возвращён обычный объект, то возвращённый объект stateProps объединяется со свойствами компонента.
Если же mapStateToProps возвращает функцию, то эта функция используется как mapStateToProps для каждого экземпляра компонента. Это может пригодиться для улучшения производительности рендеринга и для мемоизации.
Ссылки
mapDispatchToProps
объект, содержащий набор actionCreators.
может быть либо объектом, либо функцией, которая возвращает обычный объект или другую функцию.
Используется в connect
Позволяет контейнерной компоненте диспатчить изменения в store
Если в качестве аргумента mapDispatchToProps используется объект, то каждая функция в объекте будет воспринята в качестве генератора действий Redux и обёрнута в вызов метода хранилища dispatch(), что позволит вызывать его напрямую. Получившийся в результате объект с генераторами действий, dispatchProps, будет объединён со свойствами компонента.
При использовании в качестве аргумента mapDispatchToProps функции программист должен самостоятельно позаботиться о возврате объекта dispatchProps, который осуществляет привязку генераторов действий с использованием метода хранилища dispatch(). Эта функция принимает, в качестве первого параметра, метод хранилища dispatch(). Как и в случае с mapStateToProps, функция также может принимать необязательный второй параметр ownProps, который описывает маппинг с исходными свойствами, переданными компоненту.
Если эта функция возвращает другую функцию, то возвращённая функция используется в роли mapDispatchToProps, что может быть полезным для целей повышения производительности рендеринга и мемоизации.
Ссылки
Provider
компонента, оборачивается вокруг корневой компоненты (). Позволяет передавать store всем потомкам - теперь у connect() есть доступ к store
пакет react-redux предоставляет разработчику компонент , который можно использоваться для оборачивания корневого компонента приложения. Он принимает свойство store. Предполагается, что оно представляет собой ссылку на хранилище Redux, которое планируется использовать в приложении. Свойство store передаётся, в соответствии с иерархией приложения, компонентам-контейнерам, с использованием механизма контекста React:
Compose
Подход из функционального программирования Реализацию этой функции предоставляет, в частности, Redux
Позволяет объединить несколько последовательных вызовов функций. Полезно в ситуации конвейера - мы передаём данные в функцию A, результат её работы передаётся в функцию B, результат работы B передаётся в C... и так далее
compose( connect(mapStateToProps, mapDispatchToProps), withAuthRedirect ) (component)
Обрачивание идёт "снизу вверх" - вначале обернёт withAuthRedirect(), потом connect(mapStateToProps, mapDispatchToProps)()
Ссылки
Connect
connect([mapStateToProps], [mapDispatchToProps], [mergeProps], [options])
API react-redux connect() используется для создания компонентов-контейнеров, которые подключены к хранилищу Redux.
Если вам, в React-компоненте, нужно получать данные из хранилища, или требуется диспетчеризовать действия, или нужно делать и то и другое, вы можете преобразовать обычный компонент в компонент-контейнер, обернув его в компонент высшего порядка, возвращаемый функцией connect() из react-redux.
Если ваше React-приложение использует несколько хранилищ Redux, то connect() позволяет легко указывать конкретное хранилище, к которому должен быть подключён компонент-контейнер.
Ссылки
REDUX & AJAX
Запрос внутри actionCreator
самый простой вариант - делаем запрос внутри actionCreator
Например, при помощи fetch: const fetchDog = (dispatch) => { dispatch(requestDog()); return fetch('https://dog.ceo/api/breeds/image/random') .then(res => res.json()) .then( data => dispatch(requestDogSuccess(data)), err => dispatch(requestDogError()) ); };
Это простой, но очень негибкий подход.
Ядро Redux это контейнер состояния (state container), который поддерживает только синхронные потоки данных.
В случае асинхронного вызова, надо сначала дождаться ответа и затем (если не было ошибок) обновить состояние. А если у приложения сложная логика?
Для этого Redux использует промежуточные слои (middlewares) - код, который выполняется после отправки действия, но перед вызовом редюсера.
Промежуточные слои могут соединяться в цепочку вызовов для различной обработки действия (action), но на выходе обязательно должен быть простой объект (действие)
Middlewares
-
промежуточные слои Redux. Используются для реализации асинхронности в Redux
-
функция, которая запускается каждый раз при отправке action’а
-
Ядро Redux это контейнер состояния (state container), который поддерживает только синхронные потоки данных. На каждое действие, в хранилище (store) посылается объект, описывающий что произошло, затем вызывается редюсер (reducer) и состояние (state) сразу обновляется.
-
Промежуточный слой это кусок кода, который выполняется после отправки действия, но перед вызовом редюсера.
-
Промежуточные слои могут соединяться в цепочку вызовов для различной обработки действия (action), но на выходе обязательно должен быть простой объект (действие)
-
Для асинхронных операций, Redux предлагает использовать redux-thunk промежуточный слой.
Написание собственной middleware не так сложно, как может показаться, и позволяет использовать некоторые мощные средства. Например
- Хотите посылать API-запрос каждый раз, когда имя action’a начинается с FETCH_? Вы можете сделать это с помощью middleware.
- Хотите централизованное место для логирования событий в вашем аналитическом ПО? Middleware — хорошее место для этого.
- Хотите предотвратить запуск action’a в определенный момент времени? Вы можете сделать это с помощью middleware, невидимого для остальной части вашего приложения.
- Хотите перехватить action, имеющий токен JWT, и автоматически сохранить его в localStorage? Да, middleware.
Redux-thunk
-
библиотека, один из вариантов реализации middleware (промежуточный слой) для React-Redux
-
thunk = преобразователь (англ)
-
стандартный путь выполнения асинхронных операций в Redux.
-
вводит понятие функции-преобразователя, которая вызывается внутри dispatch и уже по завершении своей работы возвращает нормлаьный dispatch (вызовет необходимый метод для изменения store)
-
вызываем dispatch, как обычно. Но передаем в него не обьект, а функцию-1, которая возвращает функцию-2. В возвращаемой функции-2 есть аргумент dispatch. Теперь мы можем в этой функции-1 делать любые асинхронные операции и вызывать dispatch тогда, когда нам нужно.
-
Преимуществом использования redux-thunk является то, что компонент не знает, что выполняется асинхронное действие. Т.к. промежуточный слой автоматически передает функцию dispatch в функцию, которую возвращает генератор действий, то снаружи, для компонента, нет никакой разницы в вызове синхронных и асинхронных действий (и компонентам больше не нужно об этом беспокоиться)
-
thunk = функция, которая выполняет асинхронную операцию и на выходе диспатчит какие-то action в reducers. Саму функцию thunk тоже можно задиспатчить По сути, thunk = название функции, в которой происходит какая-то логика. Эта функция производит какие-то асинхронные действия и при этом умеет вызывать различные dispatch по результатам этих асинхронных действий. Чтобы она могла вызывать метод dispatch, он должен прийти в неё - т.е. dispatch надо передать в параметрах этой функции при её вызове Функцию thunk запускает Redux. Мы её диспатчим, а Redux store её запустит и закинет в неё свой метод dispatch.
А откуда функция thunk получит данные, которые должна обработать? Например, текст сообщения, которое она должна послать AJAX'ом на сервер? Передать эти данные как параметр функции мы не можем, т.к. вызывать её будет store. Эти данные она возьмёт из замыкания. Чтоб возникло замыкание, функцию thunk надо вернуть из некоей родительской функции (тогда thunk получит доступ к данным родительской функции). Используем родительскую функцию ThunkCreator:
- В ThunkCreator передаём данные (для передачи AJAX'ом на сервер, например),
- ThunkCreator вернёт нам thunk (уже с замыканием в котором есть нужные данные).
- полученную функцию thunk мы диспатчим в Redux store
- Redux при вызове thunk передаст в неё метод dispatch (чтоб thunk могла по результатам своей работы что-то задиспатчить в store) Т.е. по факту, мы:
- диспатчим вызов ThunkCreator, в который передаём данные
- ThunkCreator вызовет thunk (данные уже в нём благодаря замыканию),
- thunk выполнит AJAX-логику
- и по результатам вызовет какие-то dispatch, которые уйдут в reducers.
Один момент: store не умеет принимать функции (он ждёт объект со свойством type, чтоб раскидать по reducers). То есть, store не может принять thunk :( Поэтому, приходится использовать middleware (промежуточный слой) - он вклинивается между приёмником dispatch в store и моментом передачи диспатчей по reducers. Мы должны при создании store немного его перенастроить, чтоб добавить middleware в цепочку. Получается такая логика:
- если на вход поступил обычный dispatch - он проходит middleware насквозь и уходит в reducers.
- но, если на входе пришла функция (thunk) - она обрабатывается middleware, и её результаты снова отправляются на вход Store.
- если эти результаты = ещё один thunk, то процесс повторяется (да, thunk могут быть вложенными)
- если эти результаты = dispatch, то он проходит middleware насквозь и уходит в reducers.
Ссылки
- https://habr.com/ru/post/351168/
- https://monsterlessons.com/project/lessons/reduxjs-asinhronnye-eksheny-s-pomoshyu-redux-thunk
- https://tuhub.ru/posts/redux-i-thunk-vmeste-react-rukovodstvo-dlya-chajnikov
- https://www.youtube.com/watch?v=eWdnjfRu9Io
- Redux и Thunk вместе с React. Руководство для чайников.
Redux saga
-
другая библиотека, для реализации middleware (промежуточный слой) React-Redux
-
Для упрощения и улучшения сайд-эффектов в приложениях React-Redux. Прежде всего - асинхронные запросы (извлечение данных и т.д.) и нечистые вещи (доступ к кешу браузера и т.д.)
-
Их легче тестировать, на них легче реализовать сложную логику (задержки, параллельные задачи, отмена задач,)
-
Саги это дизайн паттерн, который пришел из мира распределенных транзакций, где сага управляет процессами, которые необходимо выполнять транзакционным способом, сохраняя состояние выполнения и компенсируя неудачные процессы.
-
Работают на основе функций-генераторов
-
Если говорить в общем, мы имеем сагу чья работа это следить за отправленными действиями (dispatched actions). И ещё одна сага-рабочий
-
Сага-наблюдатель (watcher saga) является ещё одним неявным слоем. Дает больше гибкости для реализации сложной логики, но иногда лишняя для простых приложений.
-
Effects. Методы внутри саг(?) возвращают не dispatch action, а объекты с инструкциями для промежуточного слоя (middleware) — отправить действие. Эти возвращаемые объекты называются Эффекты (Effects)
-
Есть альтернативы redux-saga, которые стоит попробовать. Две самых популярных это:
- redux-observable (который базируется на RxJS)
- redux-logic (также базирующийся на RxJS наблюдателях, но дающий свободу писать вашу логику в других стилях).
Axios
- инструмент для отправки ajax-запросов, основанный на промисах, очень похожий на jQuery.
- Альтернативы: got, fetch, SuperAgent, jQuery
Вариант организации AJAX (IT-Kamasutra)
для работы с серверным API & AJAX - используем axios
Изначально у нас в стэйте нет данных (например, списка задач) - мы должны получить их с сервера
Берём reducer, в котором эти данные выводятся и соответствующий action
Создаём новый action = setTasks //получить-установить задачу
В reducer пишем реакцию на этот action - добавить в state данные из объекта, который приходит с этим action
В контейнерной компоненте, в функции mapStateToProps добавляем в state компоненты задачи из общего state
В контейнерной компоненте, в функции mapDispatchToProps создаём callback для вызова этого action. При вызове этого action - он добавит задачи в state
Сам факт захода пользователя на страницу со списком задач = действие (т.е. диспатч), которое вызывает этот action.
Ссылки
Ссылки