Илья Юркин | Easy JS
1.22K subscribers
72 photos
70 links
Полезные советы от senior разработчика.

Менторство: https://bit.ly/mentor-yurkin

По всем вопросам: @i_urKing
Download Telegram
Заметки о forwardRef

В устаревшей доке React подробно описано, использование forwardRef. Но нет объяснений, зачем использовать этот HOC, когда можно просто передать ref в пропсах. На это есть несколько причин:

1. Обратная совместимость. Когда React перешел с классов на функции (FC), единственным способом переписать компонент с рефом с класса на FC был forwardRef. Это связано с тем, что в классах рефы привязывались к экземпляру класса, а у функций экземпляров нет. [Подробнее]. Если бы ref был в общем списке пропов, то при деструктуризации пропов он бы проваливался сразу дальше, а в классовых компонентах его обрабатывали отдельно.

2. Соглашение о наименовании. В приложении, без классовых компонентов, вы можете передать реф в любом пропе (кроме специальных: ref или key), например, innerRef, и при этом в дочернем компоненте не нужно использовать forwardRef. С точки зрения разработки, это проще и удобнее, так как требуется меньше HOC'ов. Однако если вы разрабатываете библиотеку или работаете в команде, то для других разработчиков может быть не очевидно, чем ваш innerRef отличается от общепринятого ref. Если вы планируете предоставить доступ к DOM верхнего элемента, делайте это через ref. Если требуется доступ к другим элементам или императивный подход, можно добавить свои пропы для рефов (подробнее в следующем пункте).

3. Инкапсуляция. По умолчанию React не позволяет компоненту обращаться к узлам DOM других компонентов. Это сделано намеренно, чтобы разработчики не могли использовать любые компоненты в императивном стиле, а только те, которые были специально разработаны для этого. Рефы – это императивная лазейка в декларативном React. Используйте их только в крайнем случае, например, когда нужно напрямую обратиться к DOM.

В новой документации React есть отличная статья про рефы и о третьем пункте в частности: https://react.dev/learn/manipulating-the-dom-with-refs

P.S. Я в этом посте делился кастомным хуком для объединения внешних и локальных рефов. Забирайте.

@js_is_easy
👍82🤔1
String Manipulation [JetBrains][VS Code]плагин, который поможет ускорить разработку. У него достаточно богатая функциональность, но лично я использую только смену регистра.

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

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

Естественно, можно настроить шорткаты, кнопка настроек на втором окне внизу.
#plugins

@js_is_easy
👍105🔥3
Внезапно для себя обнаружил, что webpack, по дефолту, во время сборки пакета, делает NODE_ENV всегда равным mode. То есть, если вы собрали библиотеку в production режиме, установили ее в проект, запустили этот проект в режиме разработки (NODE_ENV=development), то при обращении к process.env.NODE_ENV в проекте, значение будет development, а в установленной библиотеке – production.

Для проектов и большинства либ, типа UI компонентов, это не играет роли, а вот если в пакете есть проверки на окружение для оптимизаций в проде или, наоборот, расширенное логирование для разработки, то стоит обратить внимание на такую настройку вебпака, как optimization.nodeEnv. По дефолту, она делает NODE_ENV всегда равной режиму сборки. А если присвоить ей значение false, то NODE_ENV будет тянуться из окружения проекта.

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

@js_is_easy
Please open Telegram to view this post
VIEW IN TELEGRAM
👍17🤯103
Еще один интересный факт, связанный с вебпаком – это глобальная уникальность классов в CSS модулях. За их генерацию отвечает раздел modules в css-loader.

Допустим, есть два проекта с дефолтными настройками css-loader’а (localIdentName: "[path][name]__[local]--[hash:base64:5]). В этих проектах есть два разных компонента, но с одинаковым путем: src/components/Footer/Footer.modules.scss -> .container {…}. Если собрать эти проекты, то в каждом билде будет класс: src-components-Footer-Footer-module__container--x3Zn_. Эти классы уникальны в рамках одного проекта, но если соединить эти проекты через Module Federation или внешние зависимости, то получается неожиданный конфликт классов. Полагаю, что base64 использует в качестве сида путь до файла.

Я бы посоветовал добавлять в localIdentName название проекта или использовать localIdentHashSalt для избежания коллизий, если вы разрабатываете модули или UI библиотеки.

P.S. в create react app своя логика генерации классов, но эта проблема тоже присутствует.

@js_is_easy
👍83🤔3🔥2
Хотел в прошлом посте раскрыть тему умирающего CRA, но решил что он достоен отдельного.

Последний раз я создавал и сопровождал проект через CRA около 3-х лет назад. С тех пор избегаю CRA. По нескольким причинам:

Кастомизация конфига. Есть два варианта и оба такие себе. Первый – делать eject и копаться в лапше конфигов. Второй – использовать craco или react-app-rewired и снова лезть в кишки, но уже на гитхабе, чтобы написать свои патчи.

Гора скрытых зависимостей. Под капотом около 50 зависимостей и все в dependencies разделе, некоторые из них периодически ломаются. Не нужен jest или tailwind? Не переживай, CRA все равно их установит.

Больше не поддерживается. Если v4 еще худо-бедно обновляли, то на v5 забили почти сразу. Последний релиз больше года назад, свыше 1.5к issues, примерно треть из них за последний год. С каждой новой версией ноды растет риск несовместимости со свежими либами.

Любой мидл соберет нормальный конфиг вебпака с нуля. Почитайте несколько статей, это куда быстрее и приятнее, чем потом вникать в дебри CRA.

P.S. Последнее время нахваливают Vite (тут и тут). Сборщик быстрый и интересный, но в большой проект я бы его не взял – еще сырой. Если хочется что-то кроме webpack, то голый rollup.js будет постабильнее.

@js_is_easy
👍92🔥2🤔1
Задача: Объединение отсортированных массивов

Даны два целочисленных массива nums1 и nums2, отсортированных в возрастающем порядке, и два целых числа m и n, представляющие количество элементов в nums1 и nums2, соответственно.

Объедините nums1 и nums2 в один массив, отсортированный в возрастающем порядке.

Функция не должна ничего возвращать, вместо этого нужно мутировать массив nums1. Для этого, nums1 имеет длину m + n, где первые m элементов обозначают те, которые должны быть объединены, а последние n элементов установлены в 0 и должны быть проигнорированы. nums2 имеет длину n.

Задача со звездочкой: Сможете придумать алгоритм, который работает за время O(m + n)?

Пример 1:
Ввод: nums1 = [1,2,3,0,0,0], m = 3, nums2 = [2,5,6], n = 3
Вывод: [1,2,2,3,5,6]
Объяснение: Массивы, которые мы объединяем – [1,2,3] и [2,5,6].
Результат объединения –[1,2,2,3,5,6].

Пример 2:
Ввод: nums1 = [1], m = 1, nums2 = [], n = 0
Вывод: [1]
Объяснение: Массивы, которые мы объединяем – [1] и [].
Результат объединения – [1].

Пример 3:
Ввод: nums1 = [0], m = 0, nums2 = [1], n = 1
Вывод: [1]
Объяснение: Массивы, которые мы объединяем – [] и [1].
Результат объединения – [1].
Обратите внимание, что, так как m = 0, в nums1 нет элементов. 0 здесь только для того, чтобы гарантировать, что результат слияния может поместиться в nums1.

Пишите свои версии в комментариях.

Ответ с объяснением 👈

#algorithms
@js_is_easy
👍94🔥3
Типы ответов в axios

Думаю, многие используют axios для запросов. Однако стоит помнить, что по умолчанию responseType в нем установлен как json. Это означает, что все ответы с сервера будут обрабатываться как JSON-объекты (или текст, если json битый).

Для большинства случаев, это именно то, что нужно. Но, например, если нужно скачать готовый файл, такое преобразование может сыграть коварную шутку. Axios преобразует ответ в текст (поскольку JSON невалиден), и при попытке создать из преобразованного ответа Blob, получим битый файл. Проблема в том, что на вкладке Network (Сеть) и при выводе в консоль, визуально, ответ не будет отличаться: и там, и там будет выглядеть, как набор символов – разница лишь в том, что в Network ответ еще не преобразован в строку.

Фиксится довольно просто, нужно в config передать responseType: blob, тогда axios будет обрабатывать ответ соответствующим образом.

P.S. Имейте ввиду, что есть еще и другие типы ответов: arraybuffer, document, text, stream.

@js_is_easy
👍12🔥63🍾1
Работа с Singleton'ами в Webpack Module Federation

При использовании библиотек, которые создают singleton для своей работы, необходимо быть особенно внимательными во время разработки своих либ или микрофрнтов.

Например, популярная либа для переводов, i18next, создает глобальный объект для всего приложения. Поскольку приложения на Module Federation часто шарят зависимости, то все они используют один объект. Следовательно, при стандартной инициализации конфигов в обоих приложениях, они будут конфликтовать (пример на скрине).

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

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

@js_is_easy
👍9🔥32
Обработка ошибок загрузки изображений

У
тега <img /> есть коллбэк onError, который срабатывает, если загрузка контента из src не удалась. С его помощью можно избежать отображения поврежденных изображений и сделать сайт более приятным для пользователей.

Вот пример кода на React (хотя для обычного JS он будет выглядеть очень похожим):

<img
src='https://site.com/my_img.png'
onError={({ currentTarget }) => {
currentTarget.onerror = null;
currentTarget.src="./default_img.png";
}}
/>

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

@js_is_easy
👍20🔥61
Доступ к LinkedIn

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

Например, мне нравится разгребать LinkedIn, слушая подкасты или музыку на фоне. Чтобы не замедлять весь трафик через VPN, я использую это расширение для Chrome. Пока расширение активно, LinkedIn всегда доступен, а остальной интернет трафик проходит как обычно. Отключаю я его только если нужно врубить полноценный VPN, т.к. они конфликтуют.

@js_is_easy
👍72🔥1💩1
Цена React.iss.onemo

Некоторые разработчики до сих пор пренебрегают мемоизацией компонентов, относясь к ней с опаской. Их беспокоит потенциальное снижение производительности из-за мемоизации. Я сделал небольшой эксперимент, для демонстрации того, что цена React.iss.onemo ничтожно мала.

Что по результатам?

- memo компоненты с примитивными пропсами, при неизменных значениях работают в разы быстрее
- Если пропсы в memo меняются каждый рендер, то CPU перформанс снижается на пару процентов в сравнении с обычными в синтетическом тесте. В реальных компонентах разница будет почти незаметна.

Из результатов можно сделать вывод о том, что memo проигрывает обычным компонентам, только если пропсы 100% меняются каждый рендер. В основном, это пропсы с не примитивными значениями (children тоже считается).

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

Еще один, более комплексный тест: https://stackoverflow.com/a/73111523

@js_is_easy
9👍5🔥3💩1
Максимальное число параллельных запросов

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

К сожалению, серебряной пули здесь нет, но есть различные практики и обходные пути, например:
- Использовать CDN. Подойдет для ассетов, но не для JS кода
- Перейти с HTTP/1.1 на HTTP/2. Самый удобный вариант, но мало кто запаривается с переходом.
- Подходить к code splitting с умом, а не дробить приложение на миллион чанков
- Отдельный лоадер для виджетов, вместо глобального и т.д.

@js_is_easy
👍14🔥41
В любой непонятной ситуации - думай

По дороге в отпуск наконец-то дочитал книгу Максима Дорофеева «Джедайские техники». Ее мне давно советовали опытные лиды и менеджеры и я долго до нее добирался, а зря.

Из книги я почерпнул много практик и лайфхаков, и структурировал свои знания:

- стал тщательно контролировать «поток входящих» (отписался почти от всех email рассылок и оставил толко самые важные пуши)
- начал тщательнее продумывать названия задач
- упростил и реорганизовал хранение информации и многое другое.

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

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

@js_is_easy
8👍4🔥1🤔1💩1
Задача: Количество лазерных лучей в банке

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

В банке активировали защитную систему. Дан двумерный массив bank, представляющий план банка, где 0 означает пустую ячейку, а 1 – ячейку с защитным устройством.

Между двумя устройствами проходит один лазерный луч, если выполняются оба условия:

- Два устройства находятся на двух разных строках: r1 и r2, где r1 < r2.
- Для каждой строки i, где r1 < i < r2, в i-й строке нет устройств безопасности (т.е. луч всегда идет к устройствам на ближайшей строке).

Каждый луч независим, то есть один луч не мешает другим и не соединяется с ними.

Верните общее количество лазерных лучей в банке.

Пример:
Ввод: ["011001","000000","010100","001000"]
Вывод: 8
Объяснение: Между каждой из следующих пар устройств проходит луч. Всего получается 8 лучей:
* bank[0][1] -- bank[2][1]
* bank[0][1] -- bank[2][3]
* bank[0][2] -- bank[2][1]
* bank[0][2] -- bank[2][3]
* bank[0][5] -- bank[2][1]
* bank[0][5] -- bank[2][3]
* bank[2][1] -- bank[3][2]
* bank[2][3] -- bank[3][2]
Обратите внимание, что между устройствами на 0-й и 3-й строках нет лучей, потому что на 2-й строке есть устройства безопасности, что нарушает второе условие.

Больше примеров и ответ с разбором: https://telegra.ph/Kolichestvo-lazernyh-luchej-v-banke-07-12

#algorithms

@js_is_easy
👍112🔥2💩1
Заголовки в CORS запросах

Если вручную открыть вкладку “Сети” в браузере, то вы увидите все хэдеры запроса, а вот дотянуться из JS получится не до всех. Это ограничение существует из соображений безопасности [хорошая статья про корсы].

По умолчанию, для CORS запросов из браузера доступны следующие хэдеры:

- Cache-Control
- Content-Language
- Content-Type
- Expires
- Last-Modified
- Pragma
До других из JS вы просто так не дотянетесь.

Если вам нужен какой-то другой – попросите бэкендеров или девопсов добавить следующий хэдер на сервере
Access-Control-Expose-Headers: Header-Name-1, Header-Name-2

P.S. помню, как сам когда-то судорожно и безуспешно заменял axios на fetch, и игрался с настройками в надежде достать нужный хэдер))

@js_is_easy
👍243🔥1
Как CSS стили могут мешать клику по кнопке

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

Проблема нашлась в неожиданном месте: CSS стили. Через вложенный селектор на кнопку был повешен pointer-events: none. Даже не думал искать проблему в стилях, т.к. считал что pointer-events влияет только на CSS стили типа hover и cursor, оказывается он отрубает все target события элементов даже для JS

@js_is_easy
👍22💩4🤔21🔥1
Последний месяц активно ходил на собесы и в душу запал один пример, который, на мой взгляд, проверяет сразу несколько областей JS одновременно.

Задача звучит так: нужно написать функцию, которая "промисифицирует" функцию с колбэком. В этом колбэке первый аргумент — это ошибка, а второй — результат. Промисификатор должен возвращать промисифицированный вариант переданной функции (как показано на первом скрине).

Задача проверяет:
- Умение работать с переменным числом аргументов.
- Знание функций высшего порядка.
- Понимание принципов работы промисов.
- Знание работы контекста.

Что главное – это реальный пример (просто немного устаревший), а не абстрактный bar, замкнутый в foo с вопросом: “чо будет если вывести this.a ???”

Более детально можно почитать здесь https://learn.javascript.ru/promisify

@js_is_easy
👍19🔥84🤔1🤯1
Важность package-lock.json файла 🔓

Замечаю, что даже опытные разработчики иногда не уделяют должного внимания package-lock.json файлу: либо просто не коммитят его после изменения зависимостей, либо коммитят все подряд и даже не обращают внимания на содержимое. Оба пути ведут к проблемам.

Если после изменения зависимостей в package.json не закоммитить lock файл, то могут начаться проблемы с CI и билдом, т.к. зачастую, автоматизированные среды устанавливают зависимости через npm ci , а эта команда работает только с lock файлом.

Если же вы видите в гите кучу изменений в package-lock.json файле, и при этом, вы вообще не трогали package.json, то это – серьезный повод задуматься. Частая причина – это разные версии node.js и npm, обратите внимание на lockfileVersion в lock файле.

Разные package-lock.json файлы могут вести к мелким, но не всегда очевидным проблемам. Например, если установить зависимости с версией npm, отличной от проектной, то автоматически будут установлены самые свежие пакеты, удовлетворяющие зависимостям в package.json файле. А это может привести к тому, что у вашего коллеги появится баг, а у вас нет.

Не забывайте про package-lock.json, когда меняете зависимости.

@js_is_easy
👍30🔥83
Итоги прошлого года (с “небольшим” делеем) и цели на текущий

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

В свободное время часто думал над векторами развития, целями, и планами на контент, и пришел к следующему:

- Сделать канал “официально” авторским (имени меня, конечно). В самом начале я делал контент обезличенно. Но такой формат и мне наскучил, и не особо заходил аудитории. Далее я стал разбавлять посты своим мнением, но планку публичности в виде “личного бренда” так и не смог перешагнуть психологически. Тогда мне это казалось действительно большим и смелым шагом
- Нужно расширять кругозор и реагировать на происходящее в IT в целом, а не только зарываться в реакте и нюансах фронта
- Не гнаться за количеством, а сделать упор на качество. То есть, делать посты, когда реально вижу в них ценность, а не: “ну чот давно не писал, разберу-ка алгосик из чувства долга”
- Основной темой канала остается программирование. Лайфстайл фоток из качалки или басика, к вашему сожалению, пока не планируется
- Хочу передавать свои знания, и уже начал менторить. В целом, полет нормальный. Планирую черпать идеи для контента из этой сферы. Если кому интересно - подробности тут
- Запланировал создать бесплатный и открытый роадмап с проектами по React с различной сложностью и актуальными технологиями
- Ну, и последняя, но не менее важная, цель: начать развивать Youtube канал. Идеи для первых видео есть, следующий шаг – написание сценария

P.S. Мне по-прежнему не впадлу ответить на ваш вопрос в личке или комментах, поэтому не стесняйтесь😉
👍22🔥85🍾1
Алоха, друзья!

Последнее время трудился над своим первым роликом на YouTube. Вот что получилось: Ускоряем разработку в WebStorm. Конструктивная критика приветствуется 🙂

Материалы, которые обещал в видео:
🗃 Шаблоны
🤖 JS генератор

Те, кто со мной общался, знают что я любитель вебшторма, поэтому тема нарисовалась сама собой. Изначально хотел сделать статью, но потом решил, что это хорошее начало для YouTube.

#js #javascript #webstorm
🔥17👍53🍾2