Найти тему
🤝 Как подружить React.lazy с дефолтным экспортом У нас в проекте используются только именованные экспорты. Почему — писал здесь. Но React.lazy работает только с дефолтными экспортами, и раньше нам приходилось успокаивать eslint, приговаривая // eslint-disable-next-line import/no-default-export. Чтобы решить проблему старая дока реакта предлагает создать промежуточный файл, который ре-экспортирует компонент с дефолтным экспортом. export { MyComponent as default } from "./ManyComponents.js"; Но как по мне — совсем не красиво. Но что такое дефолтный экспорт? Когда мы используем динамическую загрузку модулей с помощью import, в промисе получим модуль. А дефолтный экспорт будет храниться в поле default, именованные экспорты будут храниться в соответствующих полях модуля. То есть если мы экспортируем компонент Markdown с помощью именованного экспорта, то мы сможем получить к нему доступ через одноимённое свойство Markdown на загруженном модуле. import React, { lazy, Suspense } from "react"; import { MySpinner } from "@private/design-system"; const MarkdownPreview = lazy(async () => { const module = await import('./myModule'); // преобразуем именованный экспорт в дефолтный return { default: module.Markdown }; }); export const LazyMarkdownPreview = () => { return ( <Suspense fallback={MySpinner}> <MarkdownPreview /> </Suspense> ); }; #javascript #react
1 месяц назад
return Promise 🆚 return await Promise Что будет выведено в консоль? async function test1() { console.log('start-1'); return Promise.resolve(); } async function test2() { console.log('start-2'); return await Promise.resolve(); } test1().then(() => console.log('done-1')); test2().then(() => console.log('done-2')); console.log('end'); @cherkashindev #typescript
4 месяца назад
⏭️ Соседние селекторы в CSS Сегодня утром прилетает баг - у элемента не виден горизонтальный скрол. Случилось это из-за новой фичи - появился новый компонент, из-за чего соседний немного съехал и скрол ушёл за пределы экрана. Тут я подумал, что в первый раз в жизни смогу использовать "Родственные селекторы" ~, чтобы пофиксить стили если оба элемента отображаются на странице: .new-feature-element ~ .element-with-scroll { // тут фиксим стили, они применяются к .element-with-scroll } Посмотрел на разметку, понял что .element-with-scroll всегда идёт непосредственно после .new-feature-element поэтому можно поменять на +: .new-feature-element + .element-with-scroll { // тут фиксим стили, они применяются к .element-with-scroll } Основные различия + и ~: - + выбирает только один элемент, который непосредственно следует за первым элементом. - ~ выбирает все элементы, которые следуют за первым элементом, но не обязательно сразу за ним. Посмотрел на разметку ещё раз и понял, что элементы видны всегда, а не по условию, поэтому просто пофиксил стили для .element-with-scroll без всяких соседних селекторов, но хоть потренировался. #css
6 месяцев назад
Notion 👋 Только ленивый не написал, что 9 сентября Notion прекращает работу для российских пользователей. Пару дней назад и у меня появилась желтая плашка, которая говорит “на выход”. Говорят, пользователи платных подписок точно потеряют доступ. Посмотрим, будет ли работать Notion через VPN для пользователей на бесплатном тарифе или нет. У меня пока план миграции такой: 1. Экспортировал все данные из Notion 2. Импортировал в российские аналоги - Yonote и Teamly, на случай если понадобятся старые заметки в удобно читаемом формате (ну не совсем он удобный) 3. Обмазываюсь плагинами в Obsidian и пытаюсь разобраться, что лучше “папки или связи” Теперь кратко про Obsidian: - Настроил интеграцию с гитом, чтобы бэкапить всё на гитхаб - Шаблоны Notion заменил на плагин Templates - Настроил некоторые горячие клавиши, как в VS Code. - Вместо баз данных Notion, установил плагин Projects. Он позволяет отображать данные в виде таблицы или канбан доски, как это было в Notion. - Аналога инлайн баз данных в Obsidian нет, но есть dataview. С помощью SQL подобного языка запросов Dataview Query Language, можно писать запросы ко всем заметкам в базе знаний. - Для миграции заметок из Notion можно использовать плагин Obsidian Importer. Я пока импортировал только, базу с постами для телеграмм. Импортировалось отлично. Нужно делать экспорт в HTML формате, не в Markdown. После импорта, настроил отображение с помощью плагина Projects и получилось примерно как было в Notion. И ощущение, что после этого Obsidian стал иногда подвисать, посмотрим как будет работать дальше. - Создал папку для zero-links - по сути теги в виде страниц имена, которых начинаются с двух нулей, для оптимизации поиска. - Так как инлайн баз данных нет, для ведения задач собираюсь попробовать плагин Obsidian Tasks. Если вам интересна тема Notion и как с него съехать, то вам на канал Марии. Она активно освещает текущую ситуацию, и альтернативные продукты. P.S. Расскажите, как вы ведёте базу знаний или список задач. На что мигрируете, если пользовались Notion? Тупит ли Obsidian у вас? 😅 #notion #obsidian
6 месяцев назад
У нас на проекте есть 2 основных правила для написания стилей: - У всех CSS-классов должен быть префикс dp-, выбранный по названию продукта - Мы используем БЭМ — <block>__<element>--<modifier> Однако есть большой пласт легаси стилей, где эти правила не используются. И вот, при добавлении monaco-editor’а (текстовый редактор на котором построен VS Code) в новую фичу, прилетело ишью — у горизонтального и вертикального скролл-баров разная толщина. А вся проблема в том, что наши глобальные стили для класса slider применяются к элементу с таким же классом в monaco-editor. Именно эту проблему и решают префиксы — позволяют избежать коллизии имён с библиотечными классами. #css #bem
7 месяцев назад
source-map-explorer На прошлой неделе анализировал размер главного бандла мобильной версии нашего приложения. В спешке мы накосячили с импортами и бандл раздулся. 🤔 Как анализировать? Для анализа использовал утилиту source-map-explorer. При запуске, утилита генирирует древовидную карту, с помощью которой можно увидеть, какие именно файлы попадают в бандл. К сожалению, почему именно это что-то попало в бандл утилита не говорит. 👉 В чём была проблема? Иногда мы ленимся и в файле компонента кладём какую-нибудь утилитную функцию, enum или константу, а потом где-то в другом месте импортируем их. Так вот, когда мы их импортируем, то в бандл “засасывает” не только импортируемую функцию, но и весь компонент, из-за чего размер бандла и увеличивается. P.S. А какими инструментами пользуетесь вы? #frontend #performance #tools
7 месяцев назад
Как инспектировать разметку попапов Если у вас возникают проблемы с дебагом попапов, которые открываются только по ховеру, то вот несколько простых советов: Способ 1: Открываете DevTools ⇒ Вкладка Sources ⇒ Далее наводите мышкой на свой элемент ⇒ Нажимаете F8, чтобы приостановить выполнение скрипта Способ 2: Открываете DevTools ⇒ Находите тег body или тот тег, внутри которого появляется попап и нажимаете на нём правой кнопкой ⇒ Выбираете “Break on ⇒ subtree modification” и дебажите до тех пор пока не появится попап. Способ 3: Мой любимый. Вставляете скрипт в консоль и у вас есть 3 секунды чтобы показать попап. Именно этим способом пользовался пока не узнал про первый 😄 setTimeout(() => { debugger }, 3000) #frontend #devtools
7 месяцев назад
Если бы вас спросили, что самое сложное в вашей работе, чтобы вы ответили? Я думаю одна из самых сложных вещей - читать чужой код, или свой собственный, написанный пару лет назад. 👉 Сегодня статья о том, Как избежать когнитивной перегрузки: способы оптимизации кода для разработчиков. Вот основные моменты из статьи, если вам лень читать всю статью в пятницу да ещё и летом 😅 - Когнитивная нагрузка – это объем умственной работы, необходимый разработчику для выполнения задачи. Нашим приоритетом должно быть максимальное снижение такой нагрузки в проектах. - Типы когнитивной нагрузки: - Внутренняя — изначальная сложность задачи, её нельзя уменьшить. - Внешняя — создается способом представления информации. То есть то, как написан код, решающий задачу. - Знакомая и простая вещь — не одно и то же. Они ощущаются одинаково — та же легкость перемещения по пространству без особых умственных усилий, но по совершенно разным причинам. Каждый «умный» и нестандартный прием приведет к трате времени на обучение для остальных разработчиков. После того, как они его освоят, им будет легче работать с кодом. Именно поэтому не так легко понять, как можно упростить уже знакомый код. - Нет никакой «упрощающей силы», влияющей на базу кода, кроме вашего сознательного выбора. Упрощение требует усилий, а люди часто спешат. - Отладка в два раза сложнее написания кода. Следовательно, если вы пишете код максимально хитроумно, вы по определению недостаточно умны, чтобы его отладить. - Лучшие компоненты — это те, которые обеспечивают мощную функциональность при простом интерфейсе. - Лучше большой модуль с простым API, чем много маленьких модулей с раздутым API, которые связаны между собой. - Хорошо продуманный монолит с действительно изолированными модулями часто намного удобнее и гибче, чем набор микросервисов. - DRY (Don’t repeat yourself) — хотя в целом это хорошее и фундаментальное правило, его чрезмерное использование приводит к непосильной когнитивной нагрузке. У DRY есть альтернатива — AHA Programming - Вы можете слишком рано извлечь общую функциональность, основываясь на предполагаемом сходстве, которого на самом деле в долгосрочной перспективе может не быть. Это может привести к ненужным абстракциям, которые будет непросто модифицировать или расширять. - «Небольшое копирование лучше, чем небольшая зависимость». https://habr.com/ru/companies/ncloudtech/articles/818643/ #fridayreading #essential
8 месяцев назад
Как я обычно узнаю о новых фичах в C# ❓ О новых фичах в C# я обычно узнаю с помощью подсказок Райдера — IDE для C# от JetBrains. В общем, обновился, пишу код, и Райдер предлагает что-то исправить там, где раньше все было окей. Думаю — ну ок, давай исправим. Но ощущение, что он перепутал C# c JavaScript. - string[] array = new [] { “a”, “b”, } предлагает заменить на string[] array = ["a", "b"] - Array.Empty<string>() на [] - List<string> list = new (vowels) на List<string> list = [.. vowels] Особенно интересен последний пример, очень уж напоминающий spread оператор из JS. И действительно, в C# его тоже завезли, правда по пути потеряли одну точку и назвали spread element. int[] row0 = [1, 2, 3]; int[] row1 = [4, 5, 6]; int[] row2 = [7, 8, 9]; int[] example1 = [..row0, ..row1, ..row2]; List<int> example2 = [..row0, row1[0]]; В общем, в C# 12 поднасыпали синтаксического сахара для работы с коллекциями. А ещё прихватили Primary конструктор из Scala. Это когда конструктор объявляется сразу после имени класса. public class BankAccount(string accountID, string owner) { public string AccountID { get; } = accountID; public string Owner { get; } = owner; public override string ToString() => $"Account ID: {AccountID}, Owner: {Owner}"; } Подробнее о релизе C# 12 здесь. А о том, как добавить поддержку спред оператора для кастомных коллекций описывается тут. #csharp
8 месяцев назад
Сегодня принёс вам одну из лучших статей по код ревью. В конце статьи автор упоминает фразу, которая уже давно вертелась у меня на языке, и вот здесь она отлично сформулирована. 💡 “Как это ни парадоксально, наличие постоянно полезных и ценных ревью — это признак того, что вы можете добиться большего на других этапах процесса.” - Исправлять архитектурные проблемы на фазе код ревью — слишком дорого - Люди не должны вручную искать проблемы, которые можно найти автоматически - Одна из целей код ревью — постоянно сужать его рамки, позволяя разработчикам постоянно обнаруживать, что может быть улучшено в других частях процесса разработки - Документация - Онбординг - Линтеры - Статические анализаторы - и т.д. Также ещё одна мысль, которую доносит автор — “код ревью не должно быть одинаковым для всех изменений”. 🐌 Проблемы медленных ревью - Они медленные 🤷‍♂️  — замедляют релиз на несколько часов или дней - Поверхностные — не улучшают качество кода, не происходит обмен знаниями. - Если ревью происходит медленно — разработчики начинают создавать большие пул реквесты, что увеличивает нагрузку на ревьюера и делает код ревью ещё медленнее. Подходы, которые описываются в статье Автор долго придерживался принципа Ship / Show / Ask - 🚢 Ship. Изменения простые, нет знаний, которыми стоит поделиться — льём в прод без ревью. - 🔍 Show. Если изменения простые, но в них есть что-то полезное и ими стоит поделиться (например вы написали новую функцию или компонент), то — сливайте изменения, а потом просите о код ревью. Код ревью — не блокирующий. Я обычно называю это постревью. - ❓Ask. Если вы вносите сложные изменения — дождитесь код ревью и только потом сливайте изменения. Тут код ревью блокирующий. Со временем он его немного перефразировал в Automate / Defer / Pair - 🤖 Automate. Если в изменениях нет знаний, которыми стоило бы поделиться, а в коде особо нечего улучшить — пропускаем ревью и полагаемся на линтеры, статические анализаторы и тесты. - ↪️ Defer (отложить). В зрелом и проверенном процессе разработки большинство изменений нужно ревьюить, но ревью не должно быть блокирующим. Это особенно хорошо работает, когда новая фича деплоится за фича флагом и можно спокойно получить фидбек после деплоя. - 👯 Pair. Если корректность изменений очень важна: их сложно откатить или изменения связаны с оплатой, или же изменения достаточно сложные — то вам нужно работать в паре с ревьюером, созвониться и объяснить ему изменения, чтобы он был в контексте и смог сделать ревью быстрее и качественнее. #codereview #fridayreading #weekphrase
8 месяцев назад
Мемоизация селекторов в Zustand Если вы используете Zustand, то знаете, что computed значения реализуются с помощью селекторов. const userPrs = useChartsStore((state) => { return state.pullRequests.filter(pr => pr.author.id === state.user.id); }); В примере выше: - при каждом обновлении стейта значение селектора будет вычисляться заново - это приведёт к ре-рендеру компонента, так как каждый раз мы постоянно возвращает новый массив по ссылке, а по-умолчанию используется строгое сравнение (old === new). Чтобы решить эту проблему в Zustand есть хук useShallow , который сделает “поверхностное” сравнение предыдущего и нового значения. Если они равны — ре-рендер не произойдёт. const userPrs = useChartsStore(useShallow((state) => { return state.pullRequests.filter(pr => pr.author.id === state.user.id); })); Но при этом селектор всё равно выполнится, и в большинстве случаев нам не нужно об этом заботиться. Проблема появляется если выполнение селектора занимает много времени или же он вызывается много раз в различных компонентах или других селекторах. Автор Zustand упоминает, что можно нарушить согласованность данных и просто положить вычисляемые данные в стейт (но в этом случае нужно вручную следить за их актуальностью). Также он отмечает, что для этих целей можно использовать метод memoize из его библиотеки proxy-memoize (для redux есть reselect). Аналогично immer’у proxy-memoize работает на основе Proxy. memoize запоминает предыдущий параметр функции и свойства к которым обращались в селекторе (для этого и нужен Proxy). При следующем выполнении функции, он проверит изменились ли используемые свойства, если нет — вернёт значение, вычисленное в прошлый раз. const authorSelector = memoize((state) => state.pullRequests.filter(pr => pr.author.id === state.user.id)); const userPrs = useChartsStore(authorSelector); Конечно, нужно помнить, что мемоизировать можно только “чистую” функцию — если она возвращает одни и те же значения в ответ на одни и те же аргументы Так, обернув пару селекторов в memoize, я ускорил фильтрацию пул-реквестов в более чем 20 раз (900мс ⇒ 40мс). #react #zustand #statemanagement #frontend
9 месяцев назад
На прошлой неделе проверял почту и наткнулся на рассылку от Я.Практикума “Что нельзя делать хорошему менеджеру”, где прекрасно сформулировано правило, которое я стараюсь использовать при написании кода. Смотреть на код глазами новичка. Представьте, что вы новый сотрудник и ничего не знаете кроме того, что написано в коде. - Поймёт ли он, что именно происходит в коде? - Почему это реализовано именно так?
9 месяцев назад