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

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

Связь: @pmowq
Download Telegram
Привет! Сегодня обсудим TypeScript и поймем нужно ли его учить. TypeScript — это не просто хайп, а инструмент, который делает разработку проще.

Что такое TypeScript?
TypeScript — это надстройка над JavaScript, которая добавляет статическую типизацию. Код на TS компилируется в обычный JS, но с кучей бонусов для разработчика.

Зачем использовать TypeScript?
1. Типы помогают ловить ошибки ещё на этапе написания кода. Например, если вы случайно передадите строку вместо числа, TS сразу укажет на проблему.

function add(a: number, b: number): number {
return a + b;
}
add("2", 3); // TS не даст передать строку вместо числа


2. TS делает код понятнее: типы и интерфейсы — как документация, которая всегда под рукой.

3. В больших приложениях TS спасает от хаоса. Он помогает управлять сложной логикой и предотвращает ошибки при рефакторинге.

4. Редакторы кода с TS подсказывают методы, свойства и типы.

5. Любой JS-код — это валидный TS-код. Можно добавлять типы постепенно, не переписывая проект с нуля.

Когда точно надо использовать TS?
— В проектах с большим количеством разработчиков.
— Когда нужно поддерживать сложную бизнес-логику.

Стоит ли учить TS в 2025?
Однозначно да! TypeScript — стандарт в индустрии. Плюс, знание TS жирный плюс на собеседовании.

#typescript #JavaScript
👍13🔥3
Привет! Сегодня разберёмся в разнице между extends и implements.

Что такое extends?
Ключевое слово extends используется, когда один класс наследует другой. Это значит, что он получает все свойства и методы родительского класса и может их переопределить или дополнить.


class Animal {
move() {
console.log('Moving');
}
}

class Dog extends Animal {
bark() {
console.log('Woof!');
}
}

const dog = new Dog();
dog.move(); // Moving
dog.bark(); // Woof!


Класс Dog получил доступ ко всем методам Animal.

Используем extends, когда:
— хотим переиспользовать код
— строим иерархию классов
— нужно расширить поведение базового класса


Что делает implements?
implements используется, когда класс реализует интерфейс. Интерфейс — это просто контракт, набор правил, которые класс должен соблюдать. Он не содержит никакой логики.


interface Flyable {
fly(): void;
}

class Bird implements Flyable {
fly() {
console.log('Flying');
}
}


Если в классе Bird не будет метода fly, TS сразу покажет ошибку — потому что мы обязались его реализовать.

Используем implements, когда:
— хотим задать чёткие правила, что должен уметь класс
— пишем код в стиле “контрактов”
— делаем архитектуру более предсказуемой


#typescript #JavaScript
👍9🔥5
Продолжаем разбираться с промисами. Сегодня разберем Promise.race.

Что такое Promise.race?
Promise.race - это метод, который принимает массив промисов и возвращает новый промис, который завершается или отклоняется так же, как самый быстрый промис в массиве. То есть, как только один из промисов завершается (успешно или с ошибкой), Promise.race сразу возвращает его результат, игнорируя остальные.

Пример использования
Представим, что у нас есть несколько API, и нам нужен результат от того, который ответит быстрее:

async function getFastestData() {
try {
const api1 = fetch('https://api1.example.com/data');
const api2 = fetch('https://api2.example.com/data');
const api3 = fetch('https://api3.example.com/data');

const winner = await Promise.race([api1, api2, api3]);
console.log('Самый быстрый API:', await winner.json());
} catch (error) {
console.error('Ошибка:', error);
}
}


Здесь Promise.race дождётся первого ответа от любого API. Если один из запросов завершится с ошибкой раньше остальных, Promise.race сразу перейдёт в состояние ошибки.

Когда использовать?
Например, вы отправляете запросы на несколько зеркал API, и вам нужен самый быстрый ответ.

Важно
Promise.race не отменяет остальные промисы. Даже если один промис завершился, остальные продолжают выполняться в фоне.


В одном из следующих постов разберём интересную задачу, которая встречается на некоторых собеседованиях, но не все находят её решение.

#JavaScript
👍9👏3🔥2
В прошлом посте мы разобрали Promise.race, который возвращает результат самого быстрого промиса. Сегодня решим задачу с собеседований по ограничению времени выполнения промиса с его помощью.

Задача
Ограничить время выполнения промиса, чтобы он завершался с ошибкой, если не уложился в 5 секунд.

Решение

function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Время ожидания истекло!')), ms);
});
}

async function getDataWithTimeout() {
try {
const dataPromise = fetch('https://api.example.com/data');
const result = await Promise.race([dataPromise, timeout(5000)]);
console.log('Данные:', await result.json());
} catch (error) {
console.error('Ошибка:', error.message);
}
}

getDataWithTimeout();


Как это работает?
1. Функция timeout(ms) создаёт промис, который отклоняется с ошибкой через заданное время.
2. Promise.race запускает гонку между dataPromise и timeout.
3. Если API отвечает быстрее 5 секунд — получаем данные. Если нет — срабатывает timeout, и мы ловим ошибку.

#interview
🔥11👍4
Продолжаем серию постов про SOLID. Сегодня разберём третью букву — L, которая расшифровывается как Liskov Substitution Principle или принцип подстановки Лисков.

О чём этот принцип?
Принцип гласит:
Объекты базового класса должны быть заменяемыми объектами производного класса без нарушения корректности программы.


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

Пример плохого подхода
Рассмотрим пример с платёжными системами:

class Payment {
process(amount) {
return `Обработка платежа на ${amount} рублей`;
}
}

class CreditCardPayment extends Payment {
process(amount) {
return `Оплата ${amount} рублей картой`;
}
}

class CashPayment extends Payment {
process(amount) {
throw new Error("Оплата наличными онлайн не поддерживается!");
}
}

// Использование
function makePayment(payment, amount) {
console.log(payment.process(amount));
}

const creditCard = new CreditCardPayment();
const cash = new CashPayment();

makePayment(creditCard, 100); // "Оплата 100 рублей картой"
makePayment(cash, 100); // Ошибка: Оплата наличными онлайн не поддерживается!


Здесь CashPayment нарушает принцип, потому что не может выполнить метод process, который ожидается от базового класса Payment. Это ломает логику функции makePayment.

Как улучшить?
Разделим поведение, чтобы подклассы соответствовали ожиданиям:

class Payment {
process(amount) {
throw new Error("Метод должен быть переопределён");
}
}

class OnlinePayment extends Payment {
process(amount) {
return `Онлайн-оплата на ${amount} рублей`;
}
}

class CreditCardPayment extends OnlinePayment {
process(amount) {
return `Онлайн-оплата картой на ${amount} рублей`;
}
}

class OfflinePayment extends Payment {
process(amount) {
return `Офлайн-оплата на ${amount} рублей`;
}
}

class CashPayment extends OfflinePayment {
process(amount) {
return `Оплата наличными в кассе на ${amount} рублей`;
}
}

// Использование
function makePayment(payment, amount) {
console.log(payment.process(amount));
}

const creditCard = new CreditCardPayment();
const cash = new CashPayment();

makePayment(creditCard, 100); // "Онлайн-оплата картой на 100 рублей"
makePayment(cash, 100); // "Оплата наличными в кассе на 100 рублей"


Теперь CashPayment не обязан поддерживать онлайн-платежи, а CreditCardPayment реализует оба метода. Это делает код предсказуемым и безопасным.

Что это даёт?
— Подклассы не ломают логику, заданную базовым классом.
— Легче добавлять новые подклассы без риска ошибок.
— Разделение ответственности упрощает поддержку.
— Нарушения контракта базового класса исключены.


Совет
При создании наследования проверяйте: "Может ли подкласс заменить базовый без сюрпризов?". Если нет, подумайте о разделении классов или используйте композицию.

#BestPractices #JavaScript
🔥8👍4
Часто люди говорят, что выгорели, но на деле это не выгорание, а профессиональная скука или застой.

Что такое настоящее выгорание?
Выгорание — это серьёзный психологический синдром с тремя признаками:
1. Постоянная усталость. Отдых не помогает, сил нет ни на работу, ни на личную жизнь.
2. Потеря интереса. Всё кажется бессмысленным, работа раздражает, мотивация пропадает.
3. Снижение эффективности. Даже простые задачи даются с трудом, уверенность падает.

Что делать, если выгорание накрыло?
1. Не игнорируй проблему. Проблема не решится сама, смена проекта не поможет.
2. Обратись с психологу. Выгорание часто связано не только с работой.
3. Больше отдыхай. Перестань заниматься рабочими задачами в свободное время.


Что не является выгоранием?
Если тебе скучно, это, скорее всего, застой. Его признаки:
Однообразные задачи.
Нет профессионального роста.
Работаешь на автомате.
Хочешь развития, но нет возможностей.

Вроде всё нормально. Но скучно. И мозг потихоньку тухнет 🧟

Как справиться с застоем?
1. Бери задачи, которые кажутся сложными или непривычными.
2. Участвуй в хакатонах, делай что-то для души и роста.
3. Ты не просто пишешь код — ты решаешь задачи бизнеса. И это важно.
4. Ставь цели.
5. Смени проект 🧠

Застой — это сигнал, что ты перерос текущую зону комфорта. Пора двигаться дальше и выходить из зоны комфорта ⬆️
Please open Telegram to view this post
VIEW IN TELEGRAM
5🤔5👍1
Привет) Начнем неделю с разобора as const в 🖼️

Что такое as const?
Это утверждение константности в TypeScript, которое делает объект, массив или примитив иммутабельным на уровне типов. Оно говорит компилятору, что значение не будет меняться, а его свойства или элементы становятся литеральными типами. С as const фиксируются точные значения.

Рассмотрим объект с настройками:

const config = {
mode: "development",
port: 3000,
};

// TypeScript выводит тип: { mode: string; port: number }
config.mode = "production"; // Без проблем


Здесь TypeScript считает, что mode — это просто string.

Добавим as const:

const config = {
mode: "development",
port: 3000,
} as const;

// Тип теперь: { readonly mode: "development"; readonly port: 3000 }
config.mode = "production"; // Ошибка: Cannot assign to 'mode' because it is a read-only property


Теперь config имеет точный тип, где mode — это буквально "development", а port — буквально 3000. Плюс, свойства стали readonly, что защищает их от изменений.
Тоже самое будет с массивами и другими.

Какие плюсы это даёт?
— Вы работаете с конкретными значениями, а не с общими типами (string, number и т.д.).
as const предотвращает случайные изменения объектов или массивов.
— Редактор кода лучше подсказывает значения, так как знает точные литералы.
— Меньше багов из-за неожиданных значений.

Итого:
Используйте as const, когда хотите зафиксировать точные значения объектов, массивов или примитивов, особенно в конфигурациях, словарях или наборах констант, но не злоупотребляйте)

#typescript
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥11👍5👨‍💻1
Бывают ситуации, когда нужно развернуть вложенный массив. Некоторые начинают писать reduce и вручную перебирать вложенность. В JS есть готовое решение — метод flat().

Что делает?
Метод flat() разворачивает вложенные массивы на указанную глубину, создавая новый массив.

Примеры

const arr = [1, [2, 3], [4, [5]]];
console.log(arr.flat()); // [1, 2, 3, 4, [5]]

По умолчанию разворачивает массив на 1 уровень вложенности. Но можно указать нужную глубину или даже развернуть массив полностью.

Если массив имеет несколько уровней вложенности, можно указать, на сколько уровней его развернуть:

const arr = [1, [2, 3], [4, [5, [6]]]];
console.log(arr.flat(2)); // [1, 2, 3, 4, 5, [6]]

Здесь flat(2) развернул массив на два уровня.

Если неизвестно количество уровней вложенности в массиве, то подойдет Infinity:

const arr = [1, [2, [3, [4, [5]]]]];
console.log(arr.flat(Infinity)); // [1, 2, 3, 4, 5]


Основные моменты
1. flat() не изменяет исходный массив, а возвращает новый.
2. Без аргумента разворачивает только на один уровень.

#JavaScript
🔥8👍4
Привет! Начнем неделю с разбора интересного метода padStart.

Метод padStart добавляет в начало строки указанные символы (пробелы по умолчанию), пока строка не достигнет заданной длины.

Синтаксис:

string.padStart(targetLength, padString)

1. targetLength: желаемая длина строки после дополнения.
2. padString: символы, которыми дополняется строка (по умолчанию — пробел).

Как это работает?
Если длина исходной строки меньше targetLength, метод добавляет padString в начало, пока строка не станет нужной длины. Если строка уже длиннее или равна targetLength, она остаётся без изменений.

Примеры использования
1. Форматирование чисел с ведущими нулями

const num = '42';
console.log(num.padStart(5, '0')); // "00042"


2. Форматирование даты

const day = '7';
const month = '3';
console.log(day.padStart(2, '0')); // "07"
console.log(month.padStart(2, '0')); // "03"
// Получаем: "03.07"


Основные моменты:
1. Если padString длиннее, чем нужно, он обрезается до необходимой длины.
2. Если targetLength меньше длины строки, метод возвращает строку без изменений.
3. padStart возвращает новую строку.

Поддержка браузерами: canIUse

#JavaScript
🔥9👍4
Продолжаем разбирать 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