Maxim WebDev
513 subscribers
36 photos
8 videos
39 links
Здесь узнаешь, как проходить собеседования Front-End разрабу на СНГ рынке

- Я Senior Front-End, делюсь с тобой личным опытом в IT
- Рассказываю про лайфхаки в прохождении собесов
- Показываю примеры задач с моих job-интервью

My Contact: t.me/max_webdev
Download Telegram
Более крутая альтернатива Storybook?

Недавно нашел инструмент, который может заменить Storybook во многих React-проектах. Этот инструмент называется @ladle/react.

Меня всегда напрягал Storybook следующими вещами:

1. Долгий релоуд стори после внесения изменений в код;
2. Долгая сборка билда всех сторей;
3. Куча лишнего кода, который требуется для написания стори.

Как говорится в документации ladle: "Ladle is almost 20x smaller" (имется в виду билд почти в 20 раз меньше, чем у Storybook).

Я попробовал переписать наш текущий Storybook на проекте на @ladle/react. Всего примерно было 30 сторей. Вот результат, который я заметил:

1. Релоуд стори работает намного быстрее после внесения изменений в код.
2. Размер итогового билда всех сторей от Ladle составляет 1.8 мб, у Storybook - 13.0 мб. В 7 раз меньше! Конечно не в 20, но тоже много.
3. Количество строк кода в файлах сторей уменьшилось на 10-40%. Это дает понять, что в Storybook есть много шаблонного кода, который обязателен для его работы.
4. Да и как по мне, настроить Ladle намного проще и ему не нужно куча дополнительный пакетов, как storybook.

Как-то так, буду теперь предлагать на проекте убрать Storybook и внедрить Ladle 😄
❤5🔥1
Как безопасно отрендерить HTML-строку в React? dangerouslySetInnerHTML

Всем привет! Хочу поделиться своим опытом использования dangerouslySetInnerHTML. На моем текущем проекте бэкендеры решили, что грамотно возвращать HTML-строку в запросах. Вот пример того, какая строка может прийти "Click <a href="/link" >here</a>".

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

Если у вас есть HTML-строка, и вам ее нужно отрендерить в React, то правильным выбором будет использовать атрибут dangerouslySetInnerHTML. Как говорится в самой доке React:

"dangerouslySetInnerHTML is React’s replacement for using innerHTML in the browser DOM. In general, setting HTML from code is risky because it’s easy to inadvertently expose your users to a cross-site scripting (XSS) attack".

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

<span dangerouslySetInnerHTML={{__html: DOMPurify.sanitize(htmlString)}} />

————————————

P.S. Я сделал для вас пример переиспользуемой компоненты, которую можно юзать для рендера HTML-строки в React. Можете потыкать компоненту по ссылке.

Вообще, подключение библиотеки для решения только одной небольшой проблемы - это не самый лучший вариант. Но пока что другого выхода я не нашел. В Browser API уже появился метод setHTML, который решает эту задачу с безопасностью innerHTML и dangerouslySetInnerHTML. Но этот метод еще в статусе Experimental.

Поэтому рассмотренный выше вариант кода является неплохим решением.
🔥9
Проблема с useMutation в react-query

На проекте мы используем react-query и совсем недавно я заметил необычную особенность в хуке useMutation. Проблема в том, что при повторном вызове mutateAsync, параметр data у мутации сначала изменяется на undefined, а затем уже на значение, которое пришло из запроса.

И если смотреть на это при изменении в UI, то получается "скачущий" текст, что конечно же является не самым правильным поведением для пользователя. Получается примерно так:
"data при 1-м запросе" --> undefined --> "data при 2-м запросе" --> undefined --> ""data при 3-м запросе" и т.д.

Я перекопал в интернете много вариантов, почему useMutation так работает, и как пофиксить это поведение, но ничего не нашел.

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

————————————

P.S. Если у вас есть решение получше, чем мой кастомный хук useMutationState, то оставляйте предложения в комментариях 😉
👍7
Господа программисты, мне от вас нужно инженерное решение. Я уже обсуждал варианты решения с друзьями из IT, пока нет ничего на 100% рабочего.

Проблема: бомжи терроризируют район Маяк Минска (это НЕ шутка!). Сейчас все мусорные баки закрыты в беседках из железных листов. На каждой беседке есть дверь с кодом. Код 2-значный и нужно вводить цифры на замке одновременно.

Бомжи не тупые и давно узнали коды от многих беседок с мусорными баками. Бездомные копаются в мусоре, ходят по району с ним, пугают местных жителей и детей. Ну и что детей, меня они тоже пугают.

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

Уже предложенные варианты:
1. Беседки с мусорными баками будут открываться при помощи чипа. Но проблема в том, что если бомжу в руки попадется чип, то вся система «защиты» рухнет.
2. Вызвать несколько раз милицию, чтобы они разогнали бомжей. Это чересчур легко, нам нужно инженерное решение!

А КАКИЕ У ВАС ПРЕДЛОЖЕНИЯ, ГОСПОДА?

P.S. Я уважаю всех людей, в том числе бездомных, но проблему нужно решать.
😱2
Кратко покажу мои последние диалоги в LinkedIn.

М: Здравствуйте, мне интересна ваша вакансия. Она все еще актуальна?
Р: Добрый день, Максим! Да, актуальна.
М: Подскажите вилку ЗП по вакансии?
Р: Знаете, мы отталкиваемся от ожиданий кандидата. Какие у вас ожидания?
М: N$ чистыми в месяц. Вам подходят такие условия?
Р: Ой нет, это много для нас, мы можем дать максимум X$.

Неужто они надеются найти тех, кто попросит гораздо меньше от их максимальной вилки? 🤔
🤡6🤷‍♂1😁1
✌️React Fiber для собеседований

Когда я готовлюсь к собесами, то всегда ожидаю от интервьюера глубокие вопросы по React. Одна из таких тем - это React Fiber. Я довольно часто повторяю данную тему, поэтому я сделал для себя небольшой конспект ключевых тезисов, которые помогают рассказать про Fiber на собесе. Поделюсь с вами.

Fiber это...

Fiber – переделанная реализация алгоритма Reconciliation.

Ключевые задачи Fiber:

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

Fiber Call Stack

Fiber переделывает поведение обычного Call Stack. Единицу Fiber можно рассматривать как элемент стека (либо единицу работы).

Главное преимущество Fiber Call Stack в том, что он может хранить элементы стека в памяти и исполнять эти элементы (выполнять работу) когда это необходимо, то есть Fiber Call Stack может планировать выполнение задач (scheduling). А обычный JS Call Stack так не может, он будет продолжать выполнять работу до тех пор, пока стек не опустеет.

Полезные ссылки:

1. React Fiber Architecture
2. Подробно о React Reconciliation, или Как React добился 60 fps

P.S. В следующем посте расскажу про структуру Fiber-объекта (type, key, pendingProps, memoizedProps, pendingWorkPriority и др)
👍15
✌️Структура Fiber

Ссылка на 1-ю часть про React Fiber.

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

1. type - тип компоненты. Для обычных компонент как HTML-элементов это строка ('div', 'span' и др). Для кастомных компонент (<MyComponent /> например) type - это функция или класс.

2. key - ключ компоненты, нужен, чтобы определить, необходимо ли переиспользовать компоненту. Часто key используется при рендере списков.

3. child - это то, что возвращает компонента для рендера. В примере ниже в параметре child будет храниться MyChild.

function Parent() {
return <
MyChild />
}


4. return - это Fiber элемент, к которому должна вернуться программа после обработки текущего Fiber элемента.

Из примера кода выше значением return для MyChild будет Parent.

5. pendingProps и memoizedProps - pendingProps это пропсы в начале выполнения, memoizedProps - пропсы в конце выполнения. Если данные пропсы равны, то это знак для алгоритма React, что предыдущее возвращаемое значение можно переиспользовать. А это значит, что нет необходимости в ререндере.

6. pendingWorkPriority - это число, которое обозначает приоритет единицы работы (задачи для выполнения). Чем больше число, тем ниже приоритет (всего 6 приоритетов).

Алгоритм React использует pendingWorkPriority для поиска следующей единицы работы, которую необходимо выполнить.

P.S. Это конечно же не весь список, но для базового понимания хватит.
👍9
В общем и целом, получил отказ на собеседовании. Ответ довольно частый и типичный «мы сделали выбор в пользу другого кандидата».

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

Не сконектились получается, да и много денег попросил))

А вы как часто получаете отказ в формате «мы выбрали другого кандидата»?
❤10😁1
✌️Минусы React

Я уже больше 4 лет пишу на React и поэтому могу выделить несколько его минусов. При этом я немного писал на Vue, поэтому есть с чем сравнить.

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

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

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

Если хотите больше подробностей и реальных примеров из кода про пункт 3, то наставьте 🔥 под этот пост.

3. Нужно много думать о том, как построить свое дерево компонент, чтобы предотвратить лишние ререндеры и сделать само дерево максимально оптимизированным при апдейтах. Почему разработчик должен об этом думать? Разве не проще делегировать эту задачу самой библиотеке?

4. React заставляет много думать об объектах как о ссылках. Особенно это связано с передачей объектов (или функций) в deps и пропы мемоизированной компоненты.

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

5. Бывают моменты, когда очень много зависимостей в useEffect, useMemo либо useCallback. Разработчику приходится следить, не забыл ли он добавить какую-либо зависимость, не забыл ли он удалить лишнюю зависимость.

А вы согласны со всеми 5-ю минусами React? Может у вас есть что добавить?)

Полезные ссылки

1. Кто не понял пункт 4, то почитайте мой пост “Когда применять React.memo? Оптимизации в React”
2. useContext в React
3. React Fiber для собеседований
4. How the useEffect Hook Works
🔥21👍1
Еще одно напоминание, почему я не откликаюсь на вакансии на rabota.by либо hh.ru.

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

Я лично ищу вакансии на hh.ru, но откликаюсь все равно через LinkedIn. Подробнее о том, как я это делаю через LinkedIn можно почитать здесь.
❤4
✌️Главный минус useContext и как с ним справиться

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

Я для вас сделал пример кода, по которому можно понять ключевую проблему useContext. Чтобы его посмотреть перейдите в CodeSandbox (с телефона тоже можно потыкать пример). В примере есть контекст CounterContext, который управляет двумя состояниями count1 и count2. Дело в том, что состояния не зависят друг от друга и используются в разных компонентах (count1 в Count1Component, count2 в Count2Component).

В примере нажмите на кнопку "increment count1". Вы сразу же увидите, что состояние обновилось и изменения отобразились на экране. Но дело в том, что изменение состояния в Count1Component ведет к ререндеру компоненты Count2Component (обратите внимание на параметр Renders amount). Хотя казалось бы, зачем обновлять другую компоненту, когда обновление интерфейса происходит в совсем другой части приложения?

В этом и состоит главная проблема контекста. И для ее решения один из разработчиков React Дэн Абрамов предлагает 3 варианта решения:

1. Разделить один контекст на два контекста.
2. Сделать вместо одной компоненты две: одна использует useContext, вторая обернута в memo и предотвращает лишние ререндеры из-за useContext.
3. Оставить одну компоненту, но весь JSX обернуть в useMemo и в качестве deps передать значения из контекста.

Более подробно про эти 3 пункта со всеми примерами кода можно ознакомиться здесь.

Честно говоря, я на практике использовал только 1-й вариант. Все остальные как-то не доводилось, да и большого смысла я в этом не видел. Все-таки я считаю, что контекст нужно использовать в правильном назначении, а 2 и 3 варианты больше исправляют неправильно применение useContext в проекте.

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

- Изменение темы со светлой на темную для всего приложения;
- Получение с бэка настроек и параметров для конкретной страницы. При чем эти параметры используются глубоко в дереве компонент и не очень удобно их передавать через пропсы. Главное, чтобы ваши полученные с бэка настройки не часто обновлялись;
- Управление тостами;
- и другое...
❤4👍1🔥1👏1
Хочу поделиться с вами некоторыми задачами, которые давили мне на последних собеседованиях. Позиция Middle+ / Senior 🔥. Они в принципе довольно простые, поэтому можете проверить свои знания. Я предлагаю вам не запускать код, а попробовать самим сначала решить.

Напишите потом в комментариях к этому посту, сколько вы решили задач. Я лично решил 2 из 3 :)) Вот что бывает, если не повторять базу перед собесами.

1. Задача на контекст this. Что будет выведено в консоль? Как исправить данный пример, чтобы он корректно работал?

const database = {
filterId: 'active',
users: [
{
id: 'active',
label: 'Active',
},
{
id: 'inactive',
label: 'Inactive',
},
],
getUsers: function() {
return this.users.filter(function (user) {
return user.id === this.filterId
})
}
}

console.log(database.getUsers())

2. Задача на hoisting. Что будет выведено в консоль?

(function () {
f()

f = function() {
console.log(1)
}
})()

function f() {
console.log(2)
}

f()


3. Задача на event loop. Что будет выведено в консоль? Будет ли блокироваться UI в таком случае?

sleep(2000).then(() => {
console.log(0)
})

console.log(1)

setTimeout(() => {
console.log(2)
})

function sleep(ms) {
return new Promise((resolve) => {
setTimeout(resolve, ms)
})
}

console.log(5)

function myPromise() {
abc().then((val) => {
console.log(val)
return myPromise()
})
}

function abc() {
return new Promise((resolve) => {
resolve(6)
})
}

myPromise()
❤6👍4🔥2
Хмм, JavaScript? 😏
😁8❤3
Купил себе для работы стол с подъемным механизмом.

До этого у меня был обычный стол, довольно маленький, неудобный, на него мало чего помещалось.

Сейчас у нового стола размеры 140x80 см, максимальная высота подъема 130 см.

На нем можно работать и сидя, и стоя. Я чередую каждые 30-60 минут. Сначала сидя работаю, потом когда стало неудобно сидеть, то стоя.

Еще планирую как-то кабель менеджмент настроить и кронштейн для монитора поставить 🙂
🔥15
Вспомнил недавно одну интересную и смешную историю при переписке с рекрутером. Хочу поделиться ей.

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

Я тогда работал в аутсорсе и поэтому проектов у меня было достаточно. Я выбрал 3 хороших проекта, взял оттуда некоторые файлы с кодом: примеры redux-редьюсеров, файлы для работы с api бэкенда, ui-kit компоненты с анимацией и логикой и другое. В общем и целом, выбрал те файлы и куски кода, которыми можно "похвастаться".

После эти файлы я отправил ссылкой рекрутеру. Где-то через одну неделю я получаю сообщение в формате "Максим, наши разработчики посмотрели ваш код, вынуждены вам отказать". Я уточнил "Почему?".

Мне ответили что-то вроде:

Наши разработчики не смогли запустить ни один из ваших 3-х проектов. В вашем коде есть импорты файлов, которых не существуют. Там даже не было package.json!

Как же я сгорел с этого! Вы понимаете, они хотели, чтобы я им скинул исходники всех 3-х проектов! Полностью ВЕСЬ КОД, который я писал для других заказчиков!

Я понимаю, если они бы попросили скинуть пет проекты. Но мы на звонке с рекрутером договорились на примеры кода с реальных коммерческих проектов. Короче полный угар))
🙉9🤡3😁2🤣2
Мне очень нравится рассказывать про интересные задачи, которые были у меня на собесах. А еще больше нравится рассказывать про странные задачи, которые дают интервьюеры. В этом посте покажу одну из них.

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



function getUsers() {
const users = [
{
filter: 'active',
name: 'A',
},
{
filter: 'Inactive',
name: 'A',
},
]
const appliedFilters = ['active']
const result = []

for (let i = 0; i < users.length; i += 1) {
if (appliedFilters.includes(users[i].filter)) {
result.push(users[i])
}
}

return result
}

getUsers()
getUsers()
getUsers()
// вызывается еще N раз (то есть очень много вызвовов подряд)


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

Что ж, не буду томить, скажу правильный ответ. Только перед этим попробуйте сами подумать, что в этом коде не так, а после открывайте спойлер.

ОТВЕТ:

Нужно
вынести массивы users и appliedFilters за функцию getUsers. При многократном вызове функции getUsers создается много массивов, что влияет на размер выделяемой памяти для создания объектов в JS.

Если честно, интервьюер как бы прав. Но это очень странная задача. Прям очень странная. Зачем ее давать, не очень понятно. Мне сказали, что у них в проекте таких кейсов много и многочисленное создание JS-объектов влияет на производительность их приложения.

Но этот аргумент был бы логичным, если мы бы создавали прям большие массивы при каждом вызове функции. А в примере дан массив на 2 и 1 элемента. Поэтому как по мне, задача не очень доработана.


А вы как думаете, задача по условию нормальная либо странно ее давать на собеседовании? 🧐
❤3