React Junior
207 subscribers
37 photos
462 links
Изучение React с нуля
加入频道
forceUpdate()

Метод экземпляра компонента, который форсированно вызывает метод render(), пропуская shouldComponentUpdate().

Входные параметры

👉 callback

Типичные случаи использования

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

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

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

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

Эти значения будут использоваться, если соответствующий проп не передан или равен undefined.

#документация #компоненты
displayName

Статическое свойство класса (для классового компонента) или свойство функции (для функционального компонента), которое определяет, как будет отображаться компонент в сообщениях для отладки. По умолчанию используется имя функции/класса, указанное при определении компонента.

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

👉 Основные понятия

#компоненты
#jsx
#жизненныйциклкомпонента
#виртуальныйdom
#состояние
#обработкасобытий
#потокданных
#ключи
#формы
#рефы
#hoc
#рендерпропсы
#контекст
#порталы
#хуки
#concurrentmode

👉 Управление состоянием

#управлениесостоянием
#redux
#mobx
#recoil

👉 Работа с ошибками

#обработкаошибок
#предохранители
#отладка
#проверкатипов
#typescript
#строгийрежим

👉 Оптимизация

#оптимизация
#profiler
#ленивыекомпоненты
#purecomponent

👉 Тестирование

#тестирование
#jest
#testinglibrary

👉 Серверный рендеринг

#серверныйрендеринг

👉 Стилизация

#стили
#styledcomponents
#jss
#radium
#shadowdom

👉 Распространенные задачи

#валидацияформ
#роутинг
#обменданными
#rtkquery

👉 Прочее

#документация - материалы из официальной документации
#ссылки - полезные ссылки
#началоработы - базовые знания, необходимые для начала работы
#паттерны - хорошие практики работы с React
#примерыкода - реальные примеры кода для изучения
#важно - моменты, на которые нужно обратить внимание
#ошибки - частые ошибки новичков в React
#вопросы
#подкапотом - невидимая часть React
#подключение - подключение React, а также дополнительных пакетов
#инструменты - полезные инструменты для разработки
#доступность
#проект - организация файлов и другие вопросы уровня целого проекта
#api
#безопасность
#отложено - аспекты уже пройденных тем, временно отложенные
Топ-10 ошибок, которых нужно избегать, работая с React

Статья (англ.): https://javascript.plainenglish.io/top-10-mistakes-to-avoid-when-using-react-1796711ad2a0

👉 1. Не создавать достаточное количество компонентов
👉 7. Создавать и использовать большие монолитные компоненты

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

👉 2. Изменять состояние напрямую

Установить state обычным присваиванием можно только в конструкторе классового компонента. Для всего остального существует setState.

👉 3, 9. Передавать пропсы как строки

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

👉 4. Не использовать ключи в списках

Много раз уже проговорили, почему важно устанавливать ключи.

👉 5. Забывать, что setState работает асинхронно

Да, в эту ловушку для новичков мы попались :) Решение тоже разобрали

👉 6. Чрезмерно использовать Redux

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

👉 8. Не использовать структуру папок, принятую в React-комьюнити

Поговорим про это в отдельном посте.

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

React признает только компоненты, имя которых начинается с заглавной буквы. Все остальное он обрабатывает как обычные HTML-теги.

#статьи #ошибки #redux #состояние
Файловая структура проекта

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

Структура стандартного React-проекта выглядит так:
(взято отсюда: https://javascript.plainenglish.io/top-10-mistakes-to-avoid-when-using-react-1796711ad2a0)

📂 assets
📂 components - каждый компонент в своей собственной папке
📁 product
📄 product.jsx
📁 navigation
📄 navigation.jsx
📂 containers
📂 hoc
📂 shared
📂 store

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

Документация React не настаивает на какой-то определенной структуре, говорит, что начинать нужно с чего угодно, а потом понимание само придет.
Статья в документации (рус.): https://ru.reactjs.org/docs/faq-structure.html

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

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

React работает в два этапа:

👉 Этап рендеринга (render phase).

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

На этом этапе вызываются constructor, componentWillMount (или UNSAFE_componentWillMount), componentWillReceiveProps(или UNSAFE_componentWillReceiveProps), componentWillUpdate(или UNSAFE_componentWillUpdate), getDerivedStateFromProps, shouldComponentUpdate, render. А также функция setState.

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

👉 Этап фиксации (commit phase)

React обновляет DOM, вызываются методы жизненного цикла componentDidMount и componentDidUpdate.

#документация #компоненты #жизненныйциклкомпонента
**Жизненный цикл компонента

Схема жизненного цикла классового компонента со ссылками на документацию: https://projects.wojtekmaj.pl/react-lifecycle-methods-diagram/

Монтирование

👉 constructor
👉 static getDerivedStateFromProps
👉 render
👉 componentDidMount

Обновление

👉 static getDerivedStateFromProps
👉 shouldComponentUpdate
👉 render
👉 getSnapshotBeforeUpdate
👉 componentDidUpdate

Размонтирование

👉 componentWillUnmount

Обработка ошибок

👉 static getDerivedStateFromError
👉 componentDidCatch

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

До этого мы говорили, что в React есть два типа компонентов: классовые и функциональные.

Классовые компоненты должны наследовать от React.Component или React.PureComponent.

Кроме того, компонент можно создать с помощью методов React.forwardRef(), React.lazy() и компонента высшего порядка React.memo().

#документация #компоненты
React API

Часть методов, предоставляемых библиотекой, мы уже разобрали. Оставшиеся в основном относятся к работе с DOM-элементами. В большинстве случаев с этим успешно справляется синтаксис JSX, но на всякий случай, вот список:

👉 React.createElement() - создает новый React-элемент
👉 React.createFactory() - создает фабрику React-элементов определенного типа
👉 React.cloneElement() () - клонирует React-элемент и возвращает копию
👉 React.isValidElement() - проверяет, является ли объект элементом React
👉 React.Children() - набор функций для работы со структурой this.props.children

ReactDOM API

Пакет react-dom предоставляет специфические для DOM методы, которые могут быть использованы на верхнем уровне вашего приложения. Мы уже знакомы с двумя:

👉 ReactDOM.render() - рендерит React-элемент в DOM-элемент
👉 ReactDOM.createPortal() - создает портал для рендера компонента вне родительского DOM-элемента

А вот остальные:

👉 ReactDOM.hydrate() - нужен для гидратации контейнера при серверном рендеринге. В этом мы будем разбираться позже
👉 ReactDOM.unmountComponentAtNode() - Удаляет смонтированный компонент React из DOM

ReactDOMServer

Объект с набором методов для генерирования статической разметки. Используется на Node-сервере, вернемся к нему позже.

Страница в документации (рус.): https://ru.reactjs.org/docs/react-dom-server.html

#api #документация
Различия между React и HTML

React - это JavaScript, поэтому в некоторых аспектах его синтаксис отличается от стандартного HTML. Все свойства и атрибуты DOM-элементов должны указываться в camelCase (tabIndex вместо tabindex). aria-* и data-* атрибуты остаются как есть.

* className вместо class
* dangerouslySetInnerHTML вместо innerHTML
* htmlFor вместо for
* onChange вместо onInput - для обработки ввода в реальном времени
* value для input/textarea/select
* value на теге select вместо атрибутов selected на тегах option
* style в виде объекта

#документация #важно #jsx
Synthetic Event

React оборачивает нативные события DOM в обертку Synthetic Event. Это нужно, чтобы во всех браузерах все работало одинаково. Интерфейс Synthetic Event соотвествует интерфейсу обычного Event, так что никаких проблем тут не возникает. Есть некоторые тонкости, например, синтетическое событие onMouseLeave - это на самом деле mouseout под капотом.

Нативное событие доступно в свойстве nativeEvent.

Полный список поддерживаемых событий в документации (рус.): https://ru.reactjs.org/docs/events.html#supported-events

Обработчики для них вызываются на фазе всплытия (bubbling). Чтобы ловить события на фазе перехвата (capture), к имени обработчика нужно прибавить Capture - onClickCapture вместо onClick.

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

Переходим к очень интересной теме - хукам.

Они появились в React не так давно и полностью совместимы с предыдущими версиями библиотеки.

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

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

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

С внедрением хуков React становится более "функциональным".

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

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

👉 Хуки следует вызывать только на верхнем уровне. Не вызывайте хуки внутри циклов, условий или вложенных функций.

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

👉 Хуки следует вызывать только из функциональных компонентов React. Не вызывайте хуки из обычных JavaScript-функций. (можно вызывать хуки из пользовательских хуков, о них позже)

Команда React даже создала плагин для ESLint eslint-plugin-react-hooks, который позволяет контролировать выполнение этих правил. (Плагин уже включен в Create React App)

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

#документация #хуки
Хук состояния

Встроенный хук useState является аналогом установки this.state в классовом компоненте и использования this.setState.

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

Вызов функции useState возвращает два параметра (в виде массива):
- собственно состояние (count), которое можно использовать для чтения
- функцию для обновления состояния (setCount, аналог this.setState)

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

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

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

Важно:

- при первом рендере компонента useState создает новое состояние. Его можно инициализировать дефолтным значением, передав его в хук.
- при повторных рендерах используется уже созданное ранее состояние.

#хуки #состояние #документация #примерыкода
Связь хуков с компонентами

Как именно React понимает, для какого компонента создавать внутреннее состояние при вызове хука useState?

Оказывается, React отслеживает, какой компонент рендерится в данный момент.

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

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

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

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

#хуки #подкапотом #документация
Хук эффекта

Переходим к другому встроенному хуку React - useEffect. По первому впечатлению он посложнее, чем useState, поэтому посвятим ему немного больше времени.

https://codepen.io/furrycat/pen/NWjWvmr?editors=0011

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

В классовых компонентах мы делали это в методах componentDidMount (для первого рендера) и componentDidUpdate (для последующих обновлений).

Хук useEffect позволяет делать это в одном месте. Он вызывается после КАЖДОГО рендера, включая первый.

useEffect принимает всего один аргумент - функцию. Собственно это и есть "эффект", который будет вызываться после каждого рендера.

Так как функция-эффект создается прямо внутри компонента, она имеет доступ ко всем внутренним переменным, в т.ч. к "состоянию", созданному с помощью хука useState.

#хуки #документация #примерыкода
Сброс эффекта

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

В классовых компонентах для этого предназначался метод componentWillUnmount. В функциональных эту работу выполняет хук useEffect.

https://codepen.io/furrycat/pen/OJmJxyN?editors=0011

Так получается намного логичнее, так как вся логика одного эффекта (подписка и отписка) объединяется в одном месте.

Чтобы выполнить сброс в нужное время, функция-эффект просто должна вернуть функцию-сброс.

#хуки #документация #примерыкода
Новый эффект при каждом рендере

В предыдущем примере видно, что сброс эффекта происходит не только при размонтировании компонента (как делает componentWillUnmount) - а при каждом рендере. При каждом рендере в useEffect передается новая функция-эффект, а предыдущий эффект в очереди сбрасывается.

Это сделано преднамеренно. По сути в очередь ставится уже новый эффект, независимый от прежнего, имеющий доступ к актуальным данным внутри компонента. Получается, что эффект "принадлежит" текущему рендеру.

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

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

#документация #хуки