React Junior
207 subscribers
37 photos
462 links
Изучение React с нуля
加入频道
Пропсы - только для чтения

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

#важно #компоненты #началоработы #документация
Однонаправленный поток данных

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

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

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

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

#началоработы #потокданных #компоненты #документация
Подъем состояния

Статья из документации (рус.): https://ru.reactjs.org/docs/lifting-state-up.html

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

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

👉 главный компонент Calculator, который непосредственно выполняет пересчет
👉 два компонента - поля для ввода температуры
👉 компонент с сообщением о том, достаточно ли указанной температуры для кипения воды

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

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

#началоработы #состояние #потокданных #документация
Предотвращение рендеринга

Иногда компонент может НИЧЕГО не выводить на страницу, прятаться, несмотря на то, что он был отрендерен другим компонентом. Это часто требуется для сообщений об ошибках.

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

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

#началоработы #компоненты #документация
Условный рендеринг

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

Мы уже разбирались, что в JSX можно использовать любые валидные JavaScript-выражения. В том числе и самые разные условия. Быстро пробежимся по вариантам.

👉 Вне JSX

https://codepen.io/furrycat/pen/YzZLrzE?editors=0010

Мы всегда можем произвести все нужные операции ДО инструкции return (в теле функционального компонента или метода render классового компонента). Здесь можно использовать любые JS-конструкции, включая if и switch.

Вычислив нужный компонент, просто поместите его в переменную и выведите в JSX в return.

👉 Внутри JSX

Внутри JSX можно использовать только ВЫРАЖЕНИЯ, то есть if не подходит. Вместо него подойдут:

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

https://codepen.io/furrycat/pen/ExWLway?editors=0010

☑️ тернарный оператор. Дает возможность выбрать между двумя вариантами.

https://codepen.io/furrycat/pen/MWpGEwW?editors=0010

Главное, не стремиться все запихнуть в JSX. Большие и сложные выражения лучше выносить в тело функции для лучшей читаемости.

#началоработы #jsx #примерыкода #документация
Можно ли без JSX?

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

На самом деле, все очень просто. JSX компилируется в обычный JavaScript код - вызов функции React.createElement() с описанием DOM-узла (тег, атрибуты, дочерние элементы).

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

#jsx #подкапотом #документация
Атрибут key в списках

Три правила для правильной установки ключа:

👉 ключ должен быть уникальным В ПРЕДЕЛАХ МАССИВА ЭЛЕМЕНТОВ
👉 не следует использовать в качестве ключа порядковый индекс элемента в массиве
👉 ключ должен находиться именно на том элементе, который возвращается из массива, а не на его потомках

#важно #jsx #ключи #документация
Зачем в списках нужны ключи?

При выводе массива элементов в JSX React просит указать key для каждого элемента. Другими словами, для каждого элемента нужно указать УНИКАЛЬНЫЙ (в пределах массива) идентификатор. React хочет иметь возможность различать эти элементы, зачем это ему?

Для оптимизации. Если вы добавляете в массив новые элементы, не изменяя при этом другие, React хочет просто добавить один элемент в DOM, а не перерисовывать весь список. Но для этого он должен понимать, что можно оставить как есть.

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

Статья (рус): Использование индекса в качестве ключа — это анти-паттерн

#подкапотом #jsx #ключи #ссылки
React Developer Tools

Расширение для Chrome и Firefox, которое позволяет отлаживать React-приложения.

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

Этот инструмент отображает дерево компонентов, пропсы и состояние, так что найти причину ошибки становится намного проще.

#инструменты #отладка #документация
Работа с элементами форм

Элементы форм в HTML находятся на особом положении, потому что у них есть свое собственное состояние. Но внутри React приложения удобно это состояние контролировать, то есть указывать элементу, что отображать и перехватывать все изменения. Таким образом, все состояние приложения останется в одном месте.

https://codepen.io/furrycat/pen/RwpyLMe?editors=0010

Другими словами, мы устанавливаем значение value (для инпута), а также перехватываем события change и обновляем value собственноручно.

С элементами textarea и select работаем по тому же сценарию - value и onChange. Это довольно удобно, так как унифицирует поведение разных контролов.

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

#началоработы #примерыкода #формы #документация
Обработка нескольких полей ввода

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

https://codepen.io/furrycat/pen/yLMjzEO?editors=0010

#началоработы #примерыкода #формы #документация
Композиция против наследования

Документация React с самого начала выражает свою позицию по поводу организации компонентов. Используйте композицию вместо наследования - всегда.

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

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

👉 Вставка дочерних элементов.

Используйте props.children, или другие пропсы, содержащие JSX.

👉 Специализация.

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

#паттерны #документация
Философия React

Последняя глава в разделе Основные понятия в документации называется Философия React.

👉 На примере показано, как следует делить приложение на компоненты с учетом принципа единственной ответственности (это довольно очевидно). Главное тут не бояться разделять - все может быть компонентом, даже отдельная кнопочка - для React это нормально.

👉 Объясняется, как понять, что должно быть в пропсах, а что в состоянии. Грубо говоря, то, что не меняется - это не состояние. А то, что можно вычислить на основе других данных, вообще не нужно хранить. Состояние должно быть минимальным и располагать его нужно в самом верхнем заинтересованном элементе.

👉 Выстраивается прямой (от родителей к потомкам через пропсы) и обратный (от потомков к родителям через события) потоки данных.

#паттерны #документация
Необходимые импорты

Важно: JSX - это синтаксический сахар для метода React.createElement(). Поэтому если вы используете JSX-синтаксис, React всегда должен быть в зоне видимости.

#важно #jsx #подкапотом #документация
Синтаксис расширения для передачи пропсов

Так как JSX - это обычный JS, мы можем использовать в нем синтаксис расширения для передачи пропсов как объекта.

https://codepen.io/furrycat/pen/RwpYZaz?editors=0010

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

Компоненту ButtonWrapper совсем необязательно знать полный список пропсов, чтобы все их передать в Button. Он просто заберет из объекта props все нужные ему свойства, а все остальное передаст в Button.

#примерыкода #jsx #документация
Фрагменты

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

https://codepen.io/furrycat/pen/gOmdGLR?editors=0010

Для решения этой проблемы предназначены фрагменты. Это нечто вроде невидимой обертки - элемент, который не влияет на рендер.

Фрагменты можно задать двумя способами:

- Обычный синтаксис - это тег React.Fragment.
- Краткий синтаксис - это просто угловые скобки без содержания

Второй вариант красивее, но ему нельзя задать атрибуты. А React.Fragment - можно. Хотя из атрибутов фрагментам доступен только key (если вы выводите массив фрагментов).

#началоработы #фрагменты #примерыкода #документация
Рефы

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

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

Реф нужно сначала создать с помощью метода React.createRef(), а потом передать в атрибут ref нужного элемента или компонента.

После рендеринга узел будет доступен в свойстве current рефа.

Соответственно, если вы установили ref для DOM элемента, то в свойстве ref.current будет ссылка на DOM-элемент со всеми его методами и свойствами. Если вы установили ref на компонент, то в ref.current будет ссылка на экземпляр компонента.

Исключение: нельзя использовать ref с функциональными компонентами, потому что у них нет экземпляров, в отличие от классовых.

Использование рефа с DOM-элементом: https://codepen.io/furrycat/pen/yLMEpVE?editors=0010

Использование рефа с классовым компонентом: https://codepen.io/furrycat/pen/MWpXrEP?editors=0010

#примерыкода #рефы #началоработы #документация
Коллбэк-рефы

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

https://codepen.io/furrycat/pen/ZEeRvjw?editors=0010

При этом в атрибут ref передается функция (коллбэк). Эта функция будет вызвана при монтировании компонента и получит в качестве аргумента ссылку на DOM-элемент или экземпляр React-компонента, к которому реф привязан. Внутри функции вы уже можете записать ссылку, куда необходимо.

#примерыкода #рефы #началоработы #документация
Неуправляемые компоненты

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

Например, обычные инпуты (без привязки атрибута value и отслеживания onChange). Когда пользователь что-то вводит в инпут, React об этом не знает и, следовательно, не контролирует эти данные.

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

Для доступа к неуправляемым компонентам используются рефы.

React даже дает возможность установить значение по умолчанию, с которым он потом никак не будет взаимодействовать. Это значение должно быть указано в атрибуте defaultValue или defaultChecked (в зависимости от типа поля ввода).

Небольшая статья-сравнение управляемых и неуправляемых компонентов: Контролируемые и неконтролируемые компоненты в React не должны быть сложными

#документация #паттерны #ссылки
Загрузка файлов

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

Пример из документации: https://ru.reactjs.org/redirect-to-codepen/uncontrolled-components/input-type-file

Для создания загрузчика файлов потребуется использовать реф (ссылку на DOM-элемент инпута) и File API.

#документация #паттерны #примерыкода
Взаимодействие React со сторонними библиотеками

Статья из документации (рус.): https://ru.reactjs.org/docs/integrating-with-other-libraries.html

Статья объясняет, как добавить в React-приложение плагин, написанный, например, на jQuery.

Ключевые моменты:

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

Рассматривается также противоположная задача - встраивание React-компонентов в приложение, написанное на другой технологии (Backbone).

Ключевые моменты:

👉 на странице может быть сколько угодно React-контейнеров, обрабатываемых методом ReactDOM.render()
👉 наладить связь между приложением и React-компонентом с помощью событий
👉 явно размонтировать React-компонент при удалении DOM-узла с помощью метода ReactDOM.unmountComponentAtNode()

#подключение #документация