Заметки про React – Telegram
Заметки про React
3.78K subscribers
34 photos
8 videos
485 links
Короткие заметки про React.js, TypeScript и все что с ним связано
Download Telegram
Почему нужно везде использовать мемоизацию

Stefano J. Attardi из Coinbase рассказал в своем блоге, что на проектах все компоненты оборачивают в memo. Краткие доводы в пользу такого подхода:

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

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

- Используйте useCallback и useMemo. При использовании memo необходимо мемоизировать все пропсы компонента.

https://attardi.org/why-we-memo-all-the-things/
Библиотеки для работы с формами в React

- Formik. Одна из наиболее популярных библиотек для работы с формами. Умеет отправлять форму асинхронно, поддерживает TypeScript, а также валидацию через Yup или Joi. В Formik входит набор компонентов для создания формы: Form, Field, ErrorMessage, но так же форму можно создать используя хук useFormik. 

- React Hook Form. Гибкая библиотека для создания форм черех хуки. Поддерживает TypeScript и React Native. Умеет делать валидацию полей и формы, подключается к нативным инпутам через ref атрибут. Если инпут кастомный, например, из Material UI, то предлагает использовать обертку в виде компонента Controller. 

- React Final Form. Библиотека использует паттерн Observer для обновления состояния компонентов и ре-рендера. Поддерживает валидацию полей и формы, в состав библиотеки входят как компоненты Form, Field, так и хуки. Мейнтенер библиотеки Erik Rasmussen, который также был автором Redux Form, считает ключевой фичей React Final Form высокую производительность.
Пример использования useDeferredValue

Дэн Абрамов поделился примером использования хука useDeferredValue из React 18:

function Form() {
const [state, setState] = useState({ value: "hello" })
const deferredState = useDeferredValue(state)
// ...


Используйте deferredState в компонентах, рендер которых не срочный: например, вычисление сложной логики. Это поведение напоминает debounce. Использовать state напрямую можно в элементах пользовательского ввода, например, input.
Эффективное использование контекста

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

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

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

https://kentcdodds.com/blog/how-to-use-react-context-effectively
Как работает обработка ошибок в React

Хорошая обзорная статья о том, как обрабатываются ошибки в React. Разобраны причины, почему стандартный try/catch внутри render() не работает, и почему нужно использовать Error Boundaries. 

Причина по которой стандартный try/catch не работает внутри render() заключается в принципе работы архитектуры React Fiber. Когда вызывается рендер компонента, React не сразу выполняет эту работу, а ставит в очередь работ. Рендер компонента происходит внутри workLoop, а вызов функции рендера обернут в try/catch. Если произошла ошибка рендера, то React ищет ближайшего родителя с необходимыми методами (getDerivedStateFromError или componentDidCatch) и передает ему возможность обработки ошибки.

https://habr.com/ru/company/2gis/blog/583894/
👍1
Причины рендера приложения

При оптимизации работы приложения важно уделить внимание причинам рендера компонентов. Алекс Сидоренко в своем блоге разобрал причины рендера компонентов в React и подготовил небольшую шпаргалку. Разберем основные причины рендера, и как их исправить:

- Изменение состояния в родителе. По умолчанию, если меняется состояние в компоненте, то компонент и все дочерние элементы ре-рендерятся. Чтобы не рендерить дочерние компоненты, необходимо их обернуть в memo.

- Передача нового объекта в дочерний компонент. Объекты передаются по ссылке, поэтому если в родителе объявляется объект, с каждым рендером создается новая ссылка, которая не равна предыдущей. Поэтому, если даже дочерний компонент обернут в memo, то будет происходит его ре-рендер. Используйте useMemo и useCallback для передачи объектов дочерним компонентам.

- Изменение значения контекста. Здесь похожая ситуация с первой причиной рендера. При изменении состояния компонента, дочерние компоненты ре-рендерятся. Чтобы избежать лишнего ре-рендера, нужно обернуть в memo компонент, который является дочерним у контекст-провайдера. 

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

https://alexsidorenko.com/blog/react-render-cheat-sheet/
Хуки react-router

Используя библиотеку react-router для получения текущего состояния роутера и выполнения навигации, можно использовать кастомные хуки, которые входят в react-router-dom:

- useHistory. Возвращает объект history, который позволяет изменить состояние роутера. Например, можно перенаправить пользователя на другой URL, либо вернуться на предыдущую страницу. 

- useLocation. Возвращает объект location, представляющий текущий URL. Этот хук аналогичен useState, который возвращает новый объект location при смене текущего URL.

- useParams. Хук для доступа к параметрам URL, который установлен в роуте.

- useRouteMatch. Этот хук пытается сопоставить текущий URL таким же образом, как и <Route>. Может использоваться взамен компонента <Route>, если необходимо отрендерить что-то при заданном URL.

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

https://reactrouter.com/web/api/Hooks
Кастомный React рендерер

Когда React рендерит приложение и обновляет DOM, то используется React DOM. При рендере приложения на сервере используется модуль react-dom/server. Для мобильных приложений для рендера используется модуль react-native

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

Модуль react предоставляет API для определения компонентов, а реализация находится в “рендерерах”. Для связи хуков с реализацией используется объект “диспатчер”. При вызове useState, вызов перенаправляется текущему диспатчеру рендерера. Об этом есть подробная статья в блоге Дэна Абрамова.

Для разработки своего рендерера необходимо описать объект “host config”, в котором описаны методы реализации API React. Выглядит так:


const HostConfig = {
createInstance(type, props) {
// e.g. DOM renderer returns a DOM node
},
// ...
supportsMutation: true, // it works by mutating nodes
appendChild(parent, child) {
// e.g. DOM renderer would call .appendChild() here
},
// ...
};


https://github.com/facebook/react/blob/main/packages/react-reconciler/README.md
Копия Windows 11 на React

Опенсорсная копия Windows 11, работающая в браузере и написанная с использованием React.

По большому счету представляет собой просто набор экранов Windows 11 с очень ограниченным функционалом. Но тем не менее, выглядит реалистично и очень красиво.

https://github.com/blueedgetechno/win11React
https://win11.blueedge.me/
Библиотека SWR для получения данных

SWR происходит от stale-while-revalidate, это стратегия кэширования ресурсов, в котором сначала возвращаются данные из кэша, а потом происходит запрос на получение свежих данных. Преимущество такой стратегии кэширования заключается в увеличении видимой производительности сайта, пользователям приходится меньше ждать и видеть спиннеры загрузки.

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

Особенности библиотеки: поддержка стратегии SWR, автоматическая ревалидация кэша, поддержка API Suspense. Авторами библиотеки являются разработчики Next.js.

https://swr.vercel.app/
Lazy Loading для увеличения скорости загрузки сайта

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

В React есть две функции, позволяющие легко интегрировать отложенную загрузку: React.lazy() и React.Suspense.
React.lazy() это функция, которая позволяет отрендерить динамический импорт как обычный компонент. Динамический импорт – это способ разделения кода, основной подход в реализации отложенной загрузки.
Компонент, созданный React.lazy(), загружается только тогда, когда его нужно отрендерить. В процессе загрузки компонента нужно показать какой то плейсхолдер, например, спиннер загрузки. Для этих целей предназначен React.Suspense.
На данный момент единственный вариант использования Suspense – React.lazy(). Вероятнее всего, в React 18 появится возможность использовать Suspense для обработки получения данных из API.
Отложенная загрузка по роутам

Помимо отложенной загрузки компонентов используя React.lazy() и React.Suspense можно реализовать отложенную загрузку роута приложения. Для этого необходимо конвертировать компоненты для роутов в “ленивые” компоненты роутов, а список всех роутов обернуть в Suspense.
Отложенная загрузка на сервере

До React 18 Suspense не поддерживается в серверном рендеринге. Для разделения кода на стороне сервера используйте библиотеку react-loadable.
Как работает styled-components

Joshua Comeau подробно рассказал в своем блоге о принципах работы библиотеки styled-components.

- Фабрика компонентов. При создании styled-component используется каррирование, таким образом можно использовать не только стандартные HTML теги, но и кастомные компоненты.

- При композиции styled-component поддерживается порядок вставки стилей в документ.

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

- Интерполяция стилей. Если при интерполяции стилей у двух одинаковых styled-component передать разные значения пропсов для интерполяции, то сгенерируются два класса стилей. Этот подход плох тем, что раздувает размер стилей документа и при изменении пропса потребуется генерация новых стилей. В этом случае лучше использовать CSS переменные.

https://www.joshwcomeau.com/react/demystifying-styled-components/
react-static – фреймворк для создания статических сайтов

react-static – простой в настройке генератор статических сайтов на React. Можно выделить следующие преимущества фреймворка:

- Независимость от данных. Все данные, необходимые для рендеринга сайта, заранее собираются в конфиг-файле. Источник данных может быть любой, хоть GraphQL или REST API. В компонентах доступ к данным происходит через хук или пропсы.

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

Похожие фреймворки: Gatsby и Next.js.

https://github.com/react-static/react-static
Антипаттерны React

Iskander Samatov в своём блоге рассказал об антипаттернах в React, которые чаще всего встречаются в приложениях и следует избегать:

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

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

- Передавать пропсы используя spread оператор. Когда передаются пропсы компоненту через spread оператор {...props}, то это усложняет понимание кода. Становится непонятно, какие пропсы нужны компоненту. Исключением для правила является написание компонента контейнера и HOC. 

- Объявление компонента внутри компонента. Код становится тесно связанным (high coupling), а также уменьшается производительность компонента, при каждом рендере компонента будет создаваться новый компонент.

https://isamatov.com/react-antipatterns/