Rose debug info
---------------

Подписаться на блог

Фиды

Рассылка

Фрирайтинг, заметки, шитпостинг и все подряд (18+)

Позднее Ctrl + ↑

Ошибки state-менеджмента React-компонентов

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

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

Это было проблемой, но решалась она быстро. С появлением хуков ситуация ухудшилась.

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

Парочка самых распространенных, что я видел: Эффект, который слушает проп и обновляет в след за ним стейт без каких-либо преобразований. Что-то вроде такого:

const component = ({ prop }) => {
  const [savedProp, setSavedProp] = useState(null);

  useEffect(() => {
    setSavedProp(prop);
  }, [prop]);
};

Больше логики никакой, никаких подводных камней. Мало того, что можно было бы прямо в useState прокидывать проп, так все гораздо проще — можно было бы использовать сам этот проп, вместо его сохранения в стейт! Вероятно, там когда-то был какой-то процессинг, может быть, какие-то условия, но больше этого нет, зато есть несколько ненужных обновлений компонента.

Эффект, который слушает изменения стейта, чтобы… вызвать коллбэк.

const component = ({ prop, onPropChange }) => {
  const [savedProp, setSavedProp] = useState(null);

  useEffect(() => {
    onPropChange(savedProp);
  }, [savedProp]);

  return (
    <Child prop={prop} onPropChange={setSavedProp} />
  )
};

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

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

Проблема FrontOps’а

TL;DR

  • Фронтопс — перенос девопс практик на фронтенд, так как у фронтенда есть своя специфика.
  • Основная проблема — многие фронтендеры привыкли работать с хорошими инструментами, не зная, как они работают внутри, не понимая, что происходит с кодом после того, как он написан.
  • Каждому фронтендеру нужно понимать, как собирается его проект, что происходит после сборки, как и в каком виде код доходит до пользователя, иметь представление об используемых и существующих инструментах

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

Дисклеймер: По теме я не раз говорил на митапах Webpurple, но структурировать все в текст так и не вышло нормально. Пост — просто изливание кучи мыслей, не структурирован, хотя планировалось нечто большое на несколько иную тему, в черновиках лежал больше, чем полгода, но, как говорил Тим, надо признаться, что черновики не допишутся и публиковать как есть, вот публикую. Ниже все исключительно ИМХО.

Повествование я начну как обычно — с небольшого отступления в прошлое. Когда я начинал работать как разработчик, я никогда не был против задач не для разработчика (как сейчас считается): сервер настроить, сконфигурировать прокси, сборщик из пачки sh-скриптов подкрутить, еще что-то. Да, эти задачи я решал постольку-поскольку, не использовал всякие крутые штуки и инструменты, до поры до времени ни про вагрант, ни про ансибл не слышал, но задачи решал и мне в целом нравилось.

Что касается специализаций и развитий навыков, я приверженец T-shape подхода: в основном я фронтендер, но и в DevOps с бэкендом могу. Вот благодаря этому пересечению с девопсом я гордо могу назвать себя FrontOps’ом.

Появление FrontOps

Вторая часть слова — Ops. Когда говорят про Ops, вспоминается DevOps. Несколько обидно, что почему-то в DevOps не всегда включают FrontEnd, шутка про формошлепов на это есть свои причины, но начнем с того, что такое DevOps.

DevOps — методология взаимодействия специалистов по разработке (Dev) со специалистами по информационно-технологическому обслуживанию (Ops) и интеграцию их рабочих процессов друг в друга для обеспечения качества продукта.

Wikipeadia — DevOps

В диалогах я слышал что-то вроде: «Какие еще оперейшены во фронтоне?! Че там оперировать, статика же?», — наверно, поэтому как-то это все в отдельное направление и выделилось местами. Изначально, девопс — это больше про бэкенд и все, что около него, как-то так сложилось исторически, во всяком случая я чаще сталкивался с таким, что в рамках девопс-процессов о фронтенде никто особо не думал, и это как-то само должно было решаться. Ведь что нужно бэкенду? Кластер серверов, лоадбалансинг, умный деплоймент с нулевым даунтаймом, скейлинг и много всего сложного и интересного. А что на фронтенде обычно было? Отдать статику (часто через тот же бэкенд) и все, но сейчас времена другие.

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

Проблема FrontEnd ~ Ops

Create... а дальше?

После того, как код написали, с ним еще много интересного происходит до того, как он начнет решать задачи пользователя и бизнеса, да и в процессе выполнения этих задач интересное не заканчивается. На бэкенде, разработчики в большинстве своем представляют, что с кодом происходит, после того, как он скомпилировался, так как по-другому там жить довольно сложно, надо все-таки понимать, кто там jar’ник запустил и какие и куда пакеты летают, бэкендерам даже надо понимать, что такое TCP, например (!!!).

В мире фронтенда ситуация несколько иная. На один мой комментарий в ревью «этот код не соберется вот по этой причине, вот линк, почитай, интересная штука» мне отвечали «я простой разработчик, это меня не касается» (утрирую, но примерно так было). WTF? В тот момент я понял, что что-то не так.

Откуда растут ноги этой проблемы? Из времен, когда разработка фронтенда заключалась в запуске сервера статики (и то не всегда) и изменения файликов, которые тут же отображаются в браузере, а деплой заключался в том, чтобы отдать файлики бэкендерам, а они там в jsp перегонят и сами разберутся, или загрузить файлики через FTP на другой сервер. Просто, круто, DX на высоте, код написал и все.

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

  • `git clone` — оп, код у меня;
  • `npm install` — ну, зависимости у меня, погнали;
  • `npm start` — ммм, *клик-клац-клик*, все, написал;
  • `git commit -a && git push` — отлично, таску в резолв.


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

Вроде бы задача понятна, но для многих разработчиков фронтенда она нерешаема по одной простой причине: они слишком отдалились от инструментов разработки и не успели подстроиться под их изменения, да и думать об этом часто не нужно, и «вообще пусть это девопсы делают, оперейшены все-таки». Но откуда девопс-инженерам знать о том, как код должен собираться вебпаком? А тем более о всяких узких местах, типа хранений шрифтов на том же домене, чтобы лишний dns-запрос не делался?

Много разработчиков на C++ вы знаете, которые не понимают, как линкуются библиотеки и не могут написать make-файл? А фронтендеров таких много, и это часто не проблема фронтендера, да и не проблема это, просто у нас много инструментов, которые легко устанавливаются и все делают сами.

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

Решаем проблемы

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

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

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

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

Инструменты, о которых нужно иметь представление

  • Вебпак, бабел, бандлеры, таск-раннеры (Webpack, Babel, npm-scripts, Gulp) — изучите принцип работы сборщиков и компиляторов JavaScript, TypeScript, CSS, поймите разницу между Rollup и Webpack, изучите возможности npm-scripts и Gulp (хоть его и мало кто использует сейчас).
  • Форматтеры, линтеры, статические анализаторы (ESlint, Prettier, stylelint, SonarQube) — хватит спорить на ревью о том, где должна стоять запятая, хватит мутировать иммутабельные значения, автоматизируйте проверку этого.
  • Git и хуки (Husky, lint-staged, Learn Git) — изучите Git, для вас не должны быть проблемой ребейзы, которые заканчиваются конфликтами, используйте хуки для автоматического запуска проверок.
  • Тестирование (Jest, Mocha, Jasmine, Cypress) — изучите инструменты тестирования, способы их использования, виды тестов.
  • Дополнительные инструменты для проверок проекта (size-limit, Lighthouse CI) — проверяйте перформанс-метрики вашего проекта прямо на этапе непрерывных интеграций.
  • CDN, файлы, сервера и прокси (nginx, S3, CloudFront, Cloudinary) — изучите, как файлы могут храниться и раздаваться.
  • Мониторинг (Amplify, Sentry) — настройте мониторинги, отслеживайте, что происходит с кодом при использовании.
  • Деплой, сервера, докер (Ansible, Docker, SSH) — изучите подходы к деплою, варианты запуска приложений, научитесь работать с SSH и консолью — это действительно нужный навык.
  • CI/CD (GitLab CI) — изучите принципы и подходы к организации CI/CD, сделайте полезный пайплайн для своего проекта или оптимизируйте существующий.

UI library based on Chakra-UI bundling

Recently I tried to build some UI components library that based on Chakra-UI. At first my library didn’t contain react components and compiled using typescript and it worked perfectly. But after I added React components based on Chakra-UI and migrated build to webpack, I’ve got the next error from react hooks that were called inside Chakra-UI hooks such as useTheme:

Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons:
1. You might have mismatching versions of React and the renderer (such as React DOM)
2. You might be breaking the Rules of Hooks
3. You might have more than one copy of React in the same app
See https://fb.me/react-invalid-hook-call for tips about how to debug and fix this problem.
By the way, Chakra-UI repository has this issue about this error - ((https://github.com/chakra-ui/chakra-ui/issues/495 #495)).

What is the reason for this error? useTheme hook and Chakra-UI library in whole use react library. As a result library imports react and react-dom. When webpack bundling your library that includes Chakra-UI, it includes react and react-dom to bundle of your library. In brief, you import react to your project, also you import your library with Chakra-UI that contains react too, as a result — you have at least two React copies. And in fact, it’s incorrect hook call.

How to fix it? Use only single react instance in your app. If you’re using webpack you can mark react and react-dom libraries as external libraries for your components library:

externals: {
  // mark "react" and react-dom as external packages that will import using commons
  react: 'commonjs react',
  'react-dom': 'commonjs react-dom'
},
output: {
  path: path.resolve('./'),
  libraryTarget: 'commonjs2', // set library target
  filename: '[name].js',
  library: '',
}

Now bundle of your library doesn’t contain react and react-dom. We marked libraries as external and have pure require calls but doesn’t have library in bundle:

/***/ "react":
/*!************************!*\
  !*** external "react" ***!
  \************************/
/*! no static exports found */
/***/ (function(module, exports) {
  module.exports = require("react");
/***/ }),

/***/ "react-dom":
/*!****************************!*\
  !*** external "react-dom" ***!
  \****************************/
/*! no static exports found */
/***/ (function(module, exports) {
  module.exports = require("react-dom");
/***/ })

This way also can help you if you’re using hooks and creating your own UI library without Chakra-UI.

NextJS, первый взгляд

На днях одно (пока) небольшое приложение перевел на NextJS, ибо нужен качественный SSR для SEO, а самостоятельно писать кучу хелперов слишком дорого по времени.

На первый взгляд все оказалось очень круто, что интересно, почему-то даже некоторые интерактивные элементы начали работать быстрее, не разбирался пока, какая магия их ускорила, но не отметить не мог. Миграция тоже прошла гладко, подключение к проекту было самым крутым из всех инструментов: установить через yarn, удалить конфиг webpack’а, — именно последний пункт больше всего понравился.

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

Казалось бы, роутинг должен быть сильнее всего проработан в фреймворке такого типа, но то ли я не понял, как им правильно пользоваться, то ли оно как-то странно сделано. Ладно, со ссылками пришлось повозиться, ибо в одном случае href — тот url, на который непосредственно идет редирект, а в другом случае — slug страницы, который я определил в роутере, а непосредственно ссылку прокидывать нужно в проп as.

Но вот если пушить что-то в url с динамическим роутом, то получается что-то странное. Пушу не явный конечный url, а slug описанный в роутинге, нужна квери-стринга? Ок, квери можно прокинуть в отдельный объект, они даже до чилда долетят, но вот в url они не попадут, бери ручками и прокидывай.

Тут у меня прямо явный диссонанс:

  • Ожидание: сейчас я в метод пуш прокину объект квери и текущий адрес — хоба, квери обновлен, строка адреса хранит состояние, все счастливы.
  • Реальность: прокидываю реальный адрес, прокидываю шаблон адреса, прокидываю квери-объект, новый полный адрес вместе с квери, который сам леплю чем-то, — тоже все счастливы, конечно, но большей ценой.

Действий немного, но хотелось бы каких-то изящных абстракций из коробки, а не руками их делать.

Indieweb

Впервые в индивеб я погрузился тогда, когда у меня появился интернет, правда, я тогда не знал, что это называется “индивеб”. В первые пару месяцев я сделал себе простенький сайт на narod.ru (вроде бы, да и где же еще?), потом переписал его на php, что-то туда постил, что-то там делал, старался развивать его, переписывал все стабильно минимум раз в полгода.

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

IndieWeb is a community of people building software to enable personal, independently hosted blogs to independently maintain their social data on their own web domains rather than on large, centralized social networking services.
IndieWeb

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

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

Впечатлившись, я несколько пересмотрел свой взгляд на веб в целом, а также допилил немного свой блог — теперь он поддерживает webmention’ы (можно прямо через кнопочку фидбэка справа вверху отправить вебменшен) и… ну, в остальном — пока он не совсем инди, ибо хостится, например, на Netlify, что не совсем канон, однако, самый кайф — это владение своим контентом и свой личный уникальный уголок в сети, а не, например, пачка статей на медиуме. С какой-то стороны мотивирует писать шитпостить именно сюда, а не в твиттер. Постараюсь так и делать, ведь собирался уже много раз.

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

Надеюсь, этот вебменшен для Тима успешно отправится emoji-smile
Обязательно прочитайте серию постов от Тима и присылайте вебменшены!

UPD. Узнал, что W3C опубликовала рекомендации по стандарту Webmention в этот же день 3 года назад. Интересное совпадение.

2020   blog   community

Текущая локация на странице

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

Самый первый вариант, который я рассматривал — по cron’у дергать “Find my phone” и ориентироваться на эти координаты, однако, этот вариант не особо мне понравился, ибо, во-первых, надо раскручивать API, которое может неожиданно поменяться, во-вторых, в моменты между городами cron также отрабатывает, и потом на сайте красуется какое-то неожиданное местоположение, но основная причина — сложно и ничего непонятно.

Второй вариант — сделать простой сервис с get и post ручками, развернуть его где-то в Heroku и все. Сервис я такой сделал, положил его на GitHub. Из интересного, post-метод работает через Telegram-бота — я просто кликаю кнопку и… все, ну и в MongoDB ведется лог местоположений, интересно будет потом посмотреть.

Интерфейс бота

Сервис работает как часы (еще бы одна RW-сущность плохо работала), хостится на Heroku. Осталось только интегрировать его с сайтом, но тут интересно, что сайт-то полностью статический, делать запросы лишние не очень хочется, ибо на Heroku инстансы поднимаются дольше, чем пользователи задерживаются на моем сайте. Потому все вынес в compile time, оказалось, из GraphQL все довольно удобно доставать.

В gatsby-nodes.js добавляем хук, создаем ноду в GraphQL

const fetch = require('node-fetch');
const config = require('../config');

exports.sourceNodes = async ({
  actions: { createNode },
  createContentDigest
}) => {
  const result = await fetch(config.locationUrl);
  const locationData = await result.json();

  createNode({
    ...locationData,
    id: 'author-location',
    parent: null,
    children: [],
    internal: {
      type: 'AuthorLocation',
      contentDigest: createContentDigest(locationData)
    }
  });
};

В компоненте страницы достаем это и рендерим.

query RootPage {
    authorLocation {
      city
      country
    }
}

В целом механизм работы такой:

  1. Отправляю боту местоположение.
  2. Он проверяет, что оно новое, сохраняет в MongoDB.
  3. Дергает webhook Netlify.
  4. Netlify стартует пересборку сайта.
  5. Gatsby делает запрос к сервису, получает новое местоположение.
  6. Сборка.
  7. Деплой.

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

2020   blog   development

MBP 2018 все-таки залипает

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

Так вот, на днях начал залипать, что CMD периодически не отрабатывает, а проверка нажатий с помощью EventListener’а на keyUp показала, что иногда он срабатывает несколько раз на одно нажатие. Так как в Рязани авторизованных сервисных центров нет, я решил действовать самостоятельно.

Первым делом я начал продувать клавиатуру. Точнее одну кнопку. Запшикал в нее со всех сторон половину баллона, ситуация не исправилась совсем. Ну, и решил я действовать более решительно — нашел видео на YouTube о том, как снять кнопку с клавиатуры, снял ее (к слову, оказалось очень просто) и продул. Ситуация улучшилась, но было стойкое ощущение, что не все полностью исправлено.

Интересно, что встроенной клавиатурой пользуюсь максимум пятую часть от всего времени использования ноутбука, потому вопрос залипающей клавиши не стоял для меня остро, но не оставлял меня в покое, как оно залипать-то может, если я не нажимаю кнопки?

И вот, как-то вечером я вспомнил одну интересную фразу Андрея, что может быть проблемы с кнопками возникают как раз из-за того, что на них нажимаете мало? Последние 2 дня я усиленно нажимаю кнопки (работаю без внешней клавиатуры ввиду внешних факторов) и, как я могу заметить, проблема с кнопкой то ли ушла совсем, то ли совсем не замечается.

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

UPD. В общем проблема все-таки осталась, и пришлось отдавать в сервис на ремонт. Но зато у меня теперь новый топкейс.

2019   life   mac
Ранее Ctrl + ↓