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

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

Позднее Ctrl + ↑

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 года назад. Интересное совпадение.

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

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

Самый первый вариант, который я рассматривал — по 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. Деплой.

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

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

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

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

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

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

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

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

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

HolyJS Moscow 2019

Как и планировалось, побывал на HolyJS Moscow 2019.

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

Доклады шли в несколько потоков, и выбрать что-то, что может быть тебе интересно, не составляло труда, жаль только, что пропустил Lightning talks секцию. По каждому докладу не вижу смысла что-то подробно расписывать, тут во много будет вкусовщина, но в целом впечатления только положительные, чувствуется уровень подготовки к каждому докладу и колоссальный объем работы, проделанной программным комитетом, организаторами, спикерами.

Очень здорово, что все в рамках конференции равны, общение в дискуссионных зонах действительно живое и интересное, а не, как это иногда бывает, натянутое “потому что надо”. Много впечатлений дали эти дискуссии и общение с деятелями коммьюнити, довольно сильно вдохновляет и толкает делать что-то свое и проявлять какую-то активность. В будущем нужно будет точно попробовать себя в роли спикера на конференциях и митапах за пределами Рязани — не все же одним городом ограничиваться.

Наверно, именно эта конференция стала первым поводом порадоваться тому, что Москва не так далеко от Рязани. Так что теперь, думаю, на все HolyJS в Москве я буду с удовольствием “кататься”, а может быть, и на Питерские тоже. В моем личном рейтинге HolyJS — лучшая конференция для Javascript-разработчиков в России, и это не потому что я о других не слышал, а потому что оно так и есть.

Serverless Netlify — уведомления в Telegram

Подсмотрел у Никиты в блоге кнопочку фидбэка, которая модалку вызывает, и захотел себе такую же. Запилить кнопку, которая открывает модалку на 2,5 поля не так сложно, `yarn add` что-то там, все вместе слепить и готово. А вот как его получать — другой вопрос.

Фича была крутая, JAMstack тоже, но я уехал на Эгею, так что теперь это все только в истории гита. Может, когда-то и сюда прикручу, но как-то лень

Ввиду того, что я любитель Telegram и уже делал «уведомлялки» с его помощью, то я решил не придумывать что-то новое, а просто слать уведомления себе в Telegram, но для этого нужен сервер, какой-никакой, токены ж на клиент передавать не будешь (хотя некоторые так делают, но это не мой путь). Но есть но, у меня же Gatsby, JAMstack и вот это все модное, потому просто сделать сервер — это слишком.

Будем использовать SERVERLESS, модно же и стильно (да и просто, как оказалось). Я юзаю Netlify, потому пошел на страницу «Serverless Lambda functions on Netlify» и почитал что там и как делается, сей пост что-то вроде вольного пересказа в краткой форме.

Для начала надо бы среду разработки развернуть и все необходимые пакеты поставить:

yarn add netlify-lambda
yarn add node-fetch

Теперь правим build и develop скрипты:

"lambda": "netlify-lambda serve lambda",
"build": "yarn run clean && gatsby build && netlify-lambda build lambda",

Скрипт «build» на последнем шаге будет собирать все функции, которые лежат в директории «lambda», скрипт «lamda» будет запускать dev-сервер на 9000 порту. В «netlify.toml» в секцию «build» добавляем строчку `functions = «server»`, где «server» — директория, куда будут складываться уже собранные функции.

Складываем наши функции в директорию, которую мы описали (у меня — «lambda»), в моем случае был один файлик «feedback.js», соответственно после запуска dev-сервера я могу отправлять запросы к этой функции на «localhost:9000/feedback», а в prod-режиме (уже после деплоя на Netlify) на «/.netlify/functions/feedback».

Готово, у нас есть сконфигурированное окружение и файлик, куда надо писать код, я буду писать туда код по отправке сообщений от имени Telegram-бота, в сниппете используются переменные окружения, которые можно задать в админке сайта, в «Settings -> Build & deploy -> Environment».

import fetch from 'node-fetch';

const { TELEGRAM_BOT_TOKEN, TELEGRAM_USER_ID } = process.env;

exports.handler = async ({ body, httpMethod }) => {
  if (httpMethod !== 'POST') {
    return { statusCode: 405, body: 'Method Not Allowed' };
  }

  const { page, message, type } = JSON.parse(body);

  const text = encodeURI(`New blog feedback!\n\nPage: ${page}\nType: ${type}\nMessage: ${message}`);
  const url = `https://api.telegram.org/bot${TELEGRAM_BOT_TOKEN}/sendMessage`;
  const params = `chat_id=${TELEGRAM_USER_ID}&disable_web_page_preview=1&text=${text}`;

  return fetch(`${url}?${params}`)
    .then((response) => response.json())
    .then(() => ({ statusCode: 200, body: 'ok' }))
    .catch((error) => ({ statusCode: 422, body: String(error) }));
};

Запускаем локально, стучимся postman’ом, проверяем работу и деплоим на Netlify, не забывая, что prod и dev адреса функций различаются.

Готово, модная serverless система уведомлений на NodeJS для Telegram работает и пересылает нам фидбэки, можно писать пост в блог.

Ранее Ctrl + ↓