True Frontender
1.02K subscribers
143 photos
7 videos
89 links
Сборная солянка про фронтенд.

JavaScript, React, TypeScript, HTML, CSS — здесь обсуждаем всё, что связано с веб-разработкой!

Связь: @pmowq
Download Telegram
Продолжаем разбирать SOLID. Сегодня буква I — Interface Segregation Principle (принцип разделения интерфейсов).

О чём этот принцип?
Клиенты не должны зависеть от методов, которые они не используют. Интерфейсы должны быть узкими и заточенными под конкретную задачу.

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


Пример плохого подхода

interface Device {
print(doc: string): void;
scan(doc: string): void;
fax(doc: string): void;
}

class SimplePrinter implements Device {
print(doc: string): void {
console.log(`Печатаю: ${doc}`);
}
scan(doc: string): void {
throw new Error('Не поддерживает сканирование');
}
fax(doc: string): void {
throw new Error('Не поддерживает факс');
}
}

SimplePrinter вынужден наследовать методы scan и fax, которые ему не нужны, что нарушает принцип.

Как улучшить?
Разделим интерфейсы на специфичные:

interface Printable {
print(doc: string): void;
}
interface Scannable {
scan(doc: string): void;
}
interface Faxable {
fax(doc: string): void;
}

class SimplePrinter implements Printable {
print(doc: string): void {
console.log(`Печатаю: ${doc}`);
}
}


Теперь каждый класс реализует только то, что нужно.

Что это даёт?
— Код чище и проще для поддержки
— Меньше шансов вызвать неподдерживаемый метод
— Легче расширять систему

Дробите интерфейсы на небольшие части, чтобы классы не наследовали ненужные методы.

#BestPractices #JavaScript #typescript
9👍5
Привет! Обычно для группировки я использую groupBy из Lodash, но в самом JavaScript тоже есть встроенные методы:
1. Object.groupBy
2. Map.groupBy

Пример данных с которыми будем дальше работать:

const users = [
{ name: "Аня", age: 25, city: "Москва" },
{ name: "Иван", age: 30, city: "Москва" },
{ name: "Оля", age: 22, city: "Казань" },
{ name: "Петя", age: 30, city: "Казань" },
];


Object.groupBy:

const byCity = Object.groupBy(users, user => user.city);
console.log(byCity);
// {
// "Москва": [ { name: "Аня", age: 25 }, { name: "Иван", age: 30 } ],
// "Казань": [ { name: "Оля", age: 22 }, { name: "Петя", age: 30 } ]
// }


Map.groupBy:

const byAge = Map.groupBy(users, user => user.age);
console.log(byAge);
// Map(3) {
// 25 => [ { name: "Аня", age: 25 } ],
// 30 => [ { name: "Иван", age: 30 }, { name: "Петя", age: 30 } ],
// 22 => [ { name: "Оля", age: 22 } ]
// }


Разница
1. Object.groupBy - возвращает объект, ключи всегда строки.
2. Map.groupBy - возвращает Map, где ключи могут быть любыми.

Встроенные методы — классная альтернатива без зависимостей, но Lodash остаётся для меня удобным и универсальным инструментом)

#JavaScript
👍9🔥7👏1
В проектах часто встречается рендер через логический оператор И:

{condition && <div>Component</div>}


Если condition истинно, то отрендерится компонент. Если же условие ложно, то мы ничего не увидим. Причину этого мы разбирали в этом посте(клац).

А что если мы хотим вывести компонент при одном из условий?
Можно написать так:

{condition || secondCondition && <div>Component</div>}


Но тут есть какой-то подвох... Он не всегда бросается в глаза.
Из-за приоритета операторов И(&&) выполняется раньше, чем ИЛИ(||).
Поэтому этот код на самом деле работает так:

{condition || (secondCondition && <div>Component</div>)}


В результате компонент появится только тогда, когда condition ложно, а secondCondition истинно.

Чтобы логика работала правильно, достаточно добавить группировку:

{(condition || secondCondition) && <div>Component</div>}


Теперь компонент отрендерится, если хотя бы одно условие истинно.

Но на самом деле можно упростить ещё больше. Я уже писал про условный рендер в этом посте(тык). Он выглядит понятнее и не заставляет задумываться о приоритетах:

{condition || secondCondition ? <div>Component</div> : null}


Так код читается сразу и не заставляет думать лишний раз.

#BestPractices #JavaScript
7👍5
Привет! Сегодня разберём метод Promise.withResolvers. Он упрощает работу с промисами, особенно когда нужно управлять их состоянием извне.

Что он делает?
Этот метод создаёт промис и возвращает объект, содержащий сам промис и функции для его разрешения или отклонения.

Синтаксис:

const { promise, resolve, reject } = Promise.withResolvers();

- promise — сам промис.
- resolve(value) — функция для успешного завершения промиса.
- reject(reason) — функция для отклонения промиса.

Теперь можно вызывать resolve(data) или reject(error) в любом месте, и промис перейдёт в соответствующее состояние.

Пример:
Допустим, вы ждёте событие от внешнего API

const { promise, resolve, reject } = Promise.withResolvers();

const ws = new WebSocket('wss://example.com');
ws.onmessage = event => resolve(event.data);
ws.onerror = error => reject(error);

promise
.then(data => console.log('Получено:', data))
.catch(error => console.error('Ошибка:', error));


Раньше писали так:

let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});


Преимущества withResolvers:
1. Упрощает создание управляемых промисов.
2. Убирает антипаттерн ручного присваивания.
3. Делает код более читаемым и безопасным.

Поддержку смотрите через Can I Use.

#JavaScript #BestPractices
7👍6
Привет! Сегодня разберём интересный момент в React с вызовом нескольких setState в одном обработчике.


function App() {
const [count, setCount] = useState(0);

useEffect(() => {
console.log("Effect:", count);
}, [count]);

const handleClick = () => {
setCount(count + 1);
setCount(count + 1);
setCount(count + 1);
};

return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>+3</button>
</div>
);
}


Вопрос
Что выведет консоль, и сколько раз сработает useEffect?

Разбор
1. Все три вызова setCount используют одно и то же значение count, так как React не обновляет состояние мгновенно.
2. React батчит вызовы setState в одном событии. В итоге React применяет только последний вызов, и count становится 1.
3. useEffect срабатывает один раз, так как состояние изменилось только один раз.

Вывод в консоль: Effect: 1


Чтобы каждый вызов setCount учитывал предыдущее состояние, используйте функциональное обновление:

function App() {
const [count, setCount] = useState(0);

useEffect(() => {
console.log("Effect:", count);
}, [count]);

const handleClick = () => {
setCount(prev => prev + 1);
setCount(prev => prev + 1);
setCount(prev => prev + 1);
};

return (
<div>
<p>Count: {count}</p>
<button onClick={handleClick}>+3</button>
</div>
);
}


Теперь каждый вызов setCount получает актуальное значение prev, и count увеличивается правильно, но useEffect всё равно сработает один раз, так как React батчит изменения.

#react
🔥8👍5
Сегодня разберём CSS-свойство — display: contents. Оно редко встречается, но может сильно упростить вёрстку в некоторых кейсах.

Что такое display: contents?
Это свойство заставляет элемент "исчезнуть" из рендера, но его дочерние элементы остаются и ведут себя так, будто родителя нет.

Пример
У нас есть сетка, но лишняя обёртка ломает структуру:

<style>
.wrapper {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}

.inner {
display: contents;
}

p {
background: lightblue;
padding: 10px;
}
</style>

<div class="wrapper">
<div class="inner">
<p>Привет</p>
<p>Мир</p>
</div>
</div>


Без display: contents блок .inner создаёт лишний уровень в сетке, и не попадают в ячейки .wrapper. С display: contents элемент .inner исчезает из рендера, и <p/> становятся прямыми детьми .wrapper для сетки, занимая ячейки.

Когда полезно?
- Убираем лишние обёртки, не ломая семантику HTML.
- Упрощаем работу с Grid и Flexbox, чтобы дети родителя участвовали в сетке напрямую.
- Используем для улучшения доступности.

Поддержка браузерами: Can I Use

#CSS #HTML
8👍5🔥3
Вы пользовались ИИ-редакторами? 🤯

Я недавно попробовал вайбкодить домашние проекты и это просто какой-то шок. Раньше на MVP уходило несколько дней, а сейчас буквально 1–2 часа на фронт и бэк. Главное написать качественный промт, с которым тоже помогает ИИ 😱

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

Как бы грустно это ни звучало, но зима близко 🥶 Конечно, сложно будет заменить программистов из-за качества сгенереного кода, поддержки и в целом понимания продукта, но со временем определённо станет сложнее искать работу(пока выдыхаем) 😕

#career #frontend
Please open Telegram to view this post
VIEW IN TELEGRAM
🤔7👍3👏2🔥1
Заканчиваем серии постов про SOLID! Сегодня разберём последнюю букву - D, которая расшифровывается как Dependency Inversion Principle (Принцип инверсии зависимостей).

О чём этот принцип?
Высокоуровневые модули не должны зависеть от низкоуровневых. Оба должны зависеть от абстракций. А абстракции не должны зависеть от деталей — детали зависят от абстракций.

Если проще - зависите от интерфейсов, а не от конкретных классов. Это делает код гибким и независимым от деталей реализации.

Пример плохого подхода:

class Database {
save(data: string): void {
console.log(`Сохранено в базе: ${data}`);
}
}

class UserService {
private db = new Database();

saveUser(user: string): void {
this.db.save(user);
}
}

const service = new UserService();
service.saveUser("Alice"); // Сохранено в базе: Alice


UserService жёстко привязан к Database. Хотите сохранить данные в файл или API? Придётся переписывать UserService.

Как улучшить?
Введём интерфейс и передадим зависимость через конструктор:

interface Storage {
save(data: string): void;
}

class Database implements Storage {
save(data: string): void {
console.log(`Сохранено в базе: ${data}`);
}
}

class FileStorage implements Storage {
save(data: string): void {
console.log(`Сохранено в файл: ${data}`);
}
}

class UserService {
constructor(private storage: Storage) {}

saveUser(user: string): void {
this.storage.save(user);
}
}

const dbService = new UserService(new Database());
dbService.saveUser("Alice"); // Сохранено в базе: Alice

const fileService = new UserService(new FileStorage());
fileService.saveUser("Bob"); // Сохранено в файл: Bob

Теперь UserService зависит от абстракции Storage.

Что это даёт?
- Легко менять реализацию (база, файл, API) без правок в сервисе.
- Подставляйте моки для тестов.
- Зависимости явные, меньше связей.


#BestPractices #JavaScript #typescript
🔥10👍6
Привет! Недавно мы разбирали нативный метод Object.groupBy, а сегодня разберём задачу с реализацией кастомного groupBy.

Задача
Дан массив объектов:

const users = [
{ name: 'Алиса', age: 21 },
{ name: 'Макс', age: 25 },
{ name: 'Ваня', age: 21 },
];


Нужно сгруппировать по возрасту:

groupBy(users, user => user.age);
// Результат:
// {
// 21: [{ name: 'Алиса', age: 21 }, { name: 'Ваня', age: 21 }],
// 25: [{ name: 'Макс', age: 25 }]
// }


Решение через reduce:

function groupBy(array, fn) {
return array.reduce((acc, item) => {
const key = fn(item);
(acc[key] ||= []).push(item);
return acc;
}, {});
}


Как работает?
1. reduce накапливает объект acc.
2. Для каждого item вычисляем ключ через fn(item).
3. Если ключа нет в acc, создаём массив.
4. Добавляем item в этот массив.
5. Возвращаем обновлённый acc.

#JavaScript #interview
🔥9👍4
Привет! Сегодня разберём StrictMode для разработки в React 🖼️ Он помогает находить потенциальные ошибки и улучшать качество кода.

Что делает StrictMode?
StrictMode активирует дополнительные проверки в режиме разработки:
- Дважды вызывает функции (например, useEffect, useState, конструкторы классов), чтобы выявить побочные эффекты.
- Подскажет, если вы используете методы, которые скоро удалят.
- Находит потенциальные баги, такие как мутации состояния или неправильная работа с хуками.

Как подключить?
Оберните приложение:

ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
);


Советы
- Двойной рендер может раздражать, но он спасает от багов.
- Убедитесь, что useEffect и другие хуки идемпотентны (не ломаются при повторных вызовах).
- Если добавляете StrictMode в легаси проект, будьте готовы к сюрпризам.
- Не отключайте StrictMode без веской причины.

#react #BestPractices
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥10👍3
Привет 👋
Разберём полезный и редко используемый атрибут inputmode. Он говорит браузеру, какую клавиатуру показывать на мобильных устройствах при вводе данных.

Пример:

<input type="text" inputmode="numeric" placeholder="Введите номер телефона" />


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

Доступные значения inputmode
1. text — обычная клавиатура
2. none — клавиатура не показывается
3. tel — клавиатура с цифрами и символами телефона
4. numeric — только цифры
5. decimal — цифры и точка
6. email — клавиатура с @ и .
7. url — клавиатура с / и .
8. search — клавиатура для поиска с кнопкой “Поиск”

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

Разница между type и inputmode
inputmode отвечает только за то, какую клавиатуру показать пользователю, а type определяет, какие данные реально можно ввести.

#HTML #BestPractices
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥15👍71
Привет! 👋
Хочу поделиться историей, которая началась этим летом в июле и до сих пор продолжается.

В одном из дворов мы заметили кота, который выглядел настолько плохо, что пройти мимо было просто невозможно. Худой, в ранах, с кучей проблем, о которых тогда мы ещё даже не подозревали.
Мы решили поймать его и отвезти в больницу, но это оказалось не так просто. Нам не удалось поймать его самим и пришлось искать человека, который умеет ловить животных. К счастью, нам удалось поймать его и отправить в больницу 🏥

Дальше началась самая долгая и тяжёлая часть. Кот провёл много времени в больнице, а каждый день был похож на эмоциональные качели. Постоянно всплывали новые проблемы, долгое время не было никаких улучшений, и оставалось только ждать и надеяться. Я закидывал все анализы и слова врачей в ChatGPT чтобы получить еще одно мнение по состоянию. У кота было столько серьёзных заболеваний, что сложно представить, как он жил с ними. Врачи сделали невозможное 🤯

Сейчас кот здоров настолько, насколько это вообще возможно для его возраста и того, что ему пришлось пережить. Он хорошо ест, набрал вес, живёт спокойной и размеренной жизнью. Он всё ещё боится, когда пытаешься его погладить, но его сердечко очень быстро тает ❤️ У него остались некоторые особенности по здоровью, но это не мешает ему жить и не влияет на качество жизни. Просто котик со своими особенностями)

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

Это был тяжёлый путь, но теперь у кота новая жизнь. Я очень надеюсь, что она будет долгой и без проблем со здоровьем.

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

Любите котиков ❤️
Please open Telegram to view this post
VIEW IN TELEGRAM
26🔥6👏4
Уязвимость в React ⚠️

Команда разработки React сообщила об обнаружении критической уязвимости в серверных компонентах React, позволяющей выполнять удалённое выполнение кода на уязвимых серверах, использующих React Server Components.

Что случилось?
Ошибка в механизме обработки данных в RSC даёт возможность злоумышленнику отправить специально сформированный запрос и выполнить произвольный код на сервере без авторизации.

Уязвимость получила идентификатор CVE-2025-5518 и максимальный рейтинг критичности.

Кто в зоне риска?
Затронуты проекты, использующие пакеты react-server-dom-* версий 19.0-19.2.0, в том числе через такие фреймворки, как Next.js, React Router RSC, Vite RSC плагины и другие решения с поддержкой Server Components.

Что делать? ⚠️
React уже выпустил исправления. Достаточно обновить зависимости до 19.0.1, 19.1.2 или 19.2.1. И убедитесь, что используемые фреймворки тоже подтянули фикс.

#react #security
Please open Telegram to view this post
VIEW IN TELEGRAM
👍6🔥3