React Junior
207 subscribers
37 photos
462 links
Изучение React с нуля
加入频道
Кастомный хук useDebounce

Статья (англ.): https://blog.logrocket.com/create-custom-debounce-hook-react/

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

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

В статье создается кастомный React-хук useDebounce, который на вход получает постоянно меняющееся значение (состояние поля ввода), а также значение задержки в миллисекундах. Хук возвращает debounced-значение, которое будет обновляться только после задержки.

Для реализации логики задержки используется встроенный хук useEffect с функциональностью сброса эффекта.
Хук планирует обновление debounced-значения через указанное время.

Эффект сбрасывается при каждом изменении основного состояния - значения поля ввода. То есть если исходное значение изменится раньше, то обновление debounced-значения будет отложено.

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

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

Создается контроллер и сохраняется в ref, а его свойство signal передается в запрос.
При отправке нового запроса, старый прерывается (метод `controller.abort()`).

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

AbortController на MDN: https://developer.mozilla.org/ru/docs/Web/API/AbortController
Прерывание fetch-запроса на Learn JavaScript: https://learn.javascript.ru/fetch-abort

На самом деле все просто, нужно просто в метод fetch передать в настройках signal.

#ссылки #хуки #примерыкода
👍2🔥1
Статья (англ.): https://levelup.gitconnected.com/using-typescript-infer-like-a-pro-f30ab8ab41c7

Еще немного про infer (начало здесь)

В статье приводится очень хорошее пошаговое объяснение того, как работает это ключевое слово.

Infer буквально распаковывает тип и позволяет вывести из него другой тип.

Рассматривается тот же пример с распаковкой массива и получением типа элемента (только подробнее и с анимацией).
Кроме того есть примеры с распаковкой аргументов и возвращаемого значения функций (так работает под капотом утилита ReturnType) и распаковкой ключей объекта.

Важно помнить, что infer может использоваться только в условных типах (`extends`) и после "захвата" доступен только в true-части выражения.

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

В конце задачка, которая сломала мой мозг 🤯

#typescript #infer
👍4
TypeScript. Шаблонные литералы

Статья (англ.): https://javascript.plainenglish.io/how-to-use-typescript-template-literal-types-like-a-pro-2e02a7db0bac

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

type Alignment = 'start' | 'end';
type Side = 'top' | 'right' | 'bottom' | 'left';
type AlignedPlacement = `${Side}-${Alignment}`;


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

В шаблонных строках можно также использовать утилиты, работающие со строками (Uppercase, Lowercase, Capitalize, Uncapitalize).
Также их можно использовать в условных типах в сочетании с ключевым словом infer для создания очень мощных условий.

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

А в самом конце есть прекрасный пример рекурсивной работы с типами.

#typescript
🔥3👍2
Typescript. Утилиты. Часть 5 (Pick, Omit)

Первая часть (Readonly, Required, Partial): https://yangx.top/react_junior/413
Вторая часть (Exclude, Extract, NonNullable): https://yangx.top/react_junior/429
Третья часть (Parameters, ReturnType): https://yangx.top/react_junior/430
Четвертая часть (Uppercase, Lowercase, Capitalize, Uncapitalize): https://yangx.top/react_junior/435

Утилита Pick получает два параметра: исходный тип и набор ключей. Результат работы - новый тип на основе исходного, в который входят только указанные ключи.

Эта утилита похожа на Mapped Types, только Mapped Types берут полный набор ключей из исходного типа, а для Pick набор ключей заранее указан.

Omit, соответственно, работает наоборот, берет все ключи, кроме указанных. Под капотом использует Pick и Exclude, чтобы получить набор нужных для типа ключей.

#typescript
👍1🔥1
У вас в данный момент есть активные пет-проекты?
Anonymous Poll
31%
Один
15%
Больше одного
46%
Нет, не хватает времени/сил
8%
Нет, нет желания
TypeScript. 15 самых популярных утилит

Статья (англ.): https://javascript.plainenglish.io/15-utility-types-that-every-typescript-developer-should-know-6cf121d4047c

С большинством из перечисленных в статье утилит мы уже знакомы.

Partial, Required, Readonly

Функциональность этих утилит разбирали здесь: https://yangx.top/react_junior/413
А подкапотную реализацию здесь (статья о Mapped Types): https://yangx.top/react_junior/428
Для перебора ключей используется конструкция P in keyof T.

Exclude, Extract, NonNullable

Функциональность тут: https://yangx.top/react_junior/429
Реализованы на условных типах: https://yangx.top/react_junior/431
Члены одного набора типов сопоставляются с членами второго набора с помощью конструкции T extends U ? X : Y

Pick, Omit

Функциональность и реализация тут: https://yangx.top/react_junior/442
Напоминают Mapped Types.

Parameters, ReturnType

Функциональность тут: https://yangx.top/react_junior/430
В реализации используется ключевое слово infer https://yangx.top/react_junior/439, чтобы вытащить нужные типы из полученного типа функции.

Uppercase, Lowercase, Capitalize, Uncapitalize

Функциональность здесь: https://yangx.top/react_junior/435

Record

С этой утилитой мы еще не встречались.

Она принимает два обобщенных типа: набор ключей Keys и тип значений Type. Из них формируется новый тип, в котором есть все ключи из Keys и каждому из них соответствует значение типа Type.

Условно это выглядит так:


type Keys = 101 | 102 | 103; // набор ключей
type User = { name: string; age: number }; // тип значений

// результат
type Record = {
101: User,
102: User,
103: User
}


#typescript
👍3🔥2
10 вещей, которые нужно знать о классах в TypeScript

Статья (англ.): https://levelup.gitconnected.com/10-things-you-need-to-know-about-typescript-classes-f58c57869266

Достаточно полный справочник по классам в TypeScript.

Первые пункты простые:

1. Статические поля и поля класса
2. Геттеры и сеттеры
3. Наследование классов (и реализация интерфейсов)
4. Абстрактные классы
5. Модификаторы доступа (и их сочетание с ECMAScript Private Fields)
6. Class Expressions
7. Использование дженериков в классах (https://yangx.top/react_junior/405)

Дальше посложнее: про конструкторы (и абстрактные конструкторы) и про использование в качестве типа самого класса и typeof Class. Это все пока не очень понятно, как использовать.

Но если что тип для функции-конструктора на скрине.

#typescript
👍3🔥2
Думаю, в общих чертах с TypeScript разобрались. Следующая тема для изучения - серверный рендеринг.
Пока разбираюсь в основах, в ближайшее время начнем :)
👍4🔥3
Рендеринг на стороне сервера

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

В чем принципиальная разница?

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

‼️ Какие подходы есть?

1. Просто оставить ключевые страницы сайта статическими (никаких вам SPA).

2. Делать SPA, но во время сборки генерировать статические страницы для него (библиотека react-snaphot, потом взглянем на нее). Очевидно, эти страницы будут использоваться как точки входа с хорошим SEO, а затем вступит в дело механизм SPA. Но тут есть вопросы с разными динамическими данными (апи, данные, передаваемые в урле)

3. Использовать технологии для полноценного серверного рендеринга (часть кода будет выполняться на сервере, например, Next.js).
Тут тоже есть два подхода:
- SSG (статическая генерация сайта) - что-то очень похожее на пункт 2, но покруче. Статические страницы рендерятся при сборке проекта. Доступны даже динамические урлы.
- SSR (рендеринг на стороне сервера) - что-то очень похожее на старый-добрый рендеринг на сервере, когда рендер происходит при каждом запросе.

Основное внимание мы уделим, конечно, Next.js. Также мельком глянем на react-snapshot.

#серверныйрендеринг
🔥4👍1
Next.js. Начало

Next.js - это инструмент, который должен нам помочь превратить обычное SPA на React в изоморфное приложение. То есть такое приложение, которое сначала выполнится на сервере и сгенерирует нам статические HTML-странички. А затем эти странички в браузере пользователя пройдут через процедуру гидратации и превратятся в нормальное SPA с роутингом и прочими плюшками.

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

При этом в отличие от "классического" серверного рендеринга (как на PHP-сайтах), наше приложение, по идее, не будет иметь дела с данными напрямую, даже на сервере. Оно должно обращаться к некому API-серверу, чтобы получать то, что нужно в виде JSON.

В этот раз начнем не с документации, а с видео-курса, возможно, так будет попроще.

Курс (от Владилена Минина) здесь: https://www.youtube.com/watch?v=_EOrSmjdOZQ
продолжительность 2,5 часа

Разворачиваем проект

Для быстрого разворачивания проекта на Next.js используем утилиту create-next-app (как create-react-app). Получаем шаблонный Next-проект.

Что тут есть интересного:

- папка pages, в которой будут лежать все наши странички. Тут уже лежат файлы index.tsx, api/hello.ts и пара файлов, у которых имена начинаются с нижнего подчеркивания (вероятно, они не превратятся в отдельные страницы)

- папка public - очевидно, публичные ресурсы, которые будут скопированы в собранный проект

- папка styles с файлами global.css и Home.module.css. Видимо, это система организации стилей по умолчанию. Потом посмотрим, как прикрутить что-нибудь другое

- в package.json есть скрипты dev, start и build.

Если запустить npm run dev, развернется локальный сервер с демо-страницей.

Никакого App.tsx или чего-то подобного нет, точка входа в проект, очевидно, pages/index.tsx.

#серверныйрендеринг #nextjs
👍3🔥1
Next.js. Роутинг на файлах

Роутинг в Next.js основан на файлах. Каждый файл в папке pages это отдельная страничка нашего сайта, к которой можно получить доступ по ее имени.

Например, файл pages/index.tsx - это корневая страница нашего проекта, в данном случае localhost:3000/.
Файл pages/posts.tsx - это страница localhost:3000/posts/.

В каждом таком файле находится самый обычный React-компонент (экспортируется по умолчанию).

Страницы можно вкладывать в папки, например, pages/about/contacts.tsx. Этот компонент будет доступен по адресу /about/contacts/.

#серверныйрендеринг #nextjs
👍3
Чтобы убедиться, что это не обычное SPA-приложение, а именно заранее отрендеренный на сервере документ, можно открыть код страницы или посмотреть первый запрос в панели Network - там будет HTML-документ с уже вставленным текстом. Получается, что компонент отрендерился на сервере и пришел в браузер уже в готовом виде.

Кроме того тут можно увидеть скрипт с id __NEXT_DATA_, который содержит какой-то JSON. Предположительно, это данные для гидратации приложения на клиенте.

#серверныйрендеринг #nextjs
👍4
Next.js. Динамические роуты

Next позволяет работать не только с простыми роутами типа / или /about/contacts, но и с динамическими фрагментами. Например, это могут быть страницы статей post/1, post/2 и так далее.

Динамическая часть такого роута заключается в квадратные скобки: pages/post/[id].tsx.

Чтобы получить значение динамических параметров, нужно использовать хук роутера - useRouter из пакета next/router.

const router = useRouter();
console.log(router);


Параметры находятся в поле router.query.

Теперь можно перейти на страницу http://localhost:3000/post/1.

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

#серверныйрендеринг #nextjs
👍3🔥2
Next.js. Клиентский роутинг

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

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

Адрес ссылки указывается в пропе href.

Для программной навигации нужно использовать метод Router.push

Демо: https://codesandbox.io/p/sandbox/stupefied-sutherland-r0xd40?selection=%5B%7B%22endColumn%22%3A1%2C%22endLineNumber%22%3A4%2C%22startColumn%22%3A1%2C%22startLineNumber%22%3A4%7D%5D&file=%2Fpages%2Findex.tsx

#серверныйрендеринг #nextjs #примерыкода
👍3🔥2
Next.js. Мета-данные страницы

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

Next предоставляет компонент Head, в который можно поместить любые теги, которые должны быть в секции head страницы (title, meta-теги). Размещать этот компонент можно в любом месте страницы.

#серверныйрендеринг #nextjs
🔥3👍2
Компоненты/лейауты, которые переиспользуются на разных страницах, нужно вынести за пределы папки pages, так как в pages должны лежать именно страницы.

#nextjs #серверныйрендеринг #примерыкода
👍3🔥2
Next.js. Файл pages/_document

Есть возможность изменить базовую HTML-структуру страницы, для этого нужно создать свой файл pages/_document.tsx. Компонент в этом файле может по своему усмотрению разместить компоненты Html, Head, Main и NextScript (импортируются из next/document).

- Html - корневой узел документа.
- Head - это head, но не тот же самый, который мы уже встречали в next/head. Этот head (next/document) предназначен для размещения кода, общего для всех страниц. Тот head (next/head) - для размещения отличающегося кода, например, title, на отдельных страницах.
- Внутрь Main будет вставляться активная страница. Все, что находится вне Main, не будет инициализировано, поэтому сюда не нужно добавлять какую-то логику.
- NextScript - это скрипты, которые нужны для работы Next.js.

Компонентам можно добавлять атрибуты (например, lang).

Страница в документации (англ.): https://nextjs.org/docs/advanced-features/custom-document

#nextjs #серверныйрендеринг
👍4🔥1
Next.js. Файл pages/_app

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

Кроме того, именно в этом месте мы можем добавить глобальные стили для приложения.

UPD: также, здесь, кажется, хорошее место для размещения провайдеров глобальных контекстов.

Для этого создаем файл pages/_app.tsx.

В базовом варианте он выглядит очень просто:

- принимает пропсы Component (компонент активной страницы) и pageProps
- и просто рендерит этот компонент с этими пропсами.

Страница в документации (англ.): https://nextjs.org/docs/advanced-features/custom-app

#nextjs #серверныйрендеринг
👍6
Next.js. Стили

Что по стилям?

1. Если нам нужны какие-то глобальные стили, то можно импортировать .css-файлы в наш кастомный компонент App (файл pages/_app.tsx). Но такой трюк пройдет только здесь. При попытке импортировать .css-файл с глобальными стилями на какую-нибудь страницу или в компонент, вылетит ошибка:

Global CSS cannot be imported from files other than your Custom App. Due to the Global nature of stylesheets, and to avoid conflicts, Please move all first-party global CSS imports to pages/_app.js. Or convert the import to Component-Level CSS (CSS Modules).

2. Однако внутри компонентов/страниц мы можем импортировать стили сторонних библиотек (из node_modules).

3. Если необходимо импортировать свои стили в компонент, нам предлагают пользоваться CSS-модулями (разбирали их здесь).

4. Можно использовать препроцессоры, например, SCSS, но понадобится установить пакет sass. Поддерживается даже прямой импорт и использование SASS-переменных в файлах компонентов.

5. Документация уверяет, что поддерживаются любые решения CSS-in-JS. Действительно, со Styled Components проблем не возникло. Глобальные стили (createGlobalStyles) следует размещать в файле _app.tsx.

6. Также нам предлагают использовать styled-jsx, который поддерживается из коробки. При этом можно писать стили прямо в JSX-разметке, внутри тега style как обычный CSS (документация тут).

#nextjs #серверныйрендеринг #стили
👍2🔥1
Нашелся старый пост с субъективным сравнением разных способов стилизации React-приложений: https://yangx.top/react_junior/229

Надо признать, что с тех пор мои предпочтения немного изменились и в большинстве своих проектов я использую Styled Components. Синтаксис стал привычнее, и в целом это решение весьма удобное. НО! - только если стилей мало. Для больших простыней по-прежнему предпочитаю модули + препроцессоры.

#стили
👍2