1137
Сборная солянка про фронтенд. JavaScript, React, TypeScript, HTML, CSS — здесь обсуждаем всё, что связано с веб-разработкой! Связь: @pmowq
Один из моих любимых утилити-типов в TypeScript — ReturnType. Сегодня пост о нём.
Что делает ReturnType?ReturnType<T> — извлекает тип возвращаемого значения из функции T.
Пример:
function getUser() {
return { id: 1, name: 'Василий' };
}
type User = ReturnType<typeof getUser>;
// { id: number, name: string }
User всегда будет соответствовать тому, что возвращает getUser. ReturnType — можно получить тип возвращаемого значения хука и использовать его в пропсах компонента.
// Какой-то хук с логикой
function useUserData() {}
// Пропсы компонента
interface Props {
data: ReturnType<typeof useUserData>;
};
function UserInfo({ data }: Props) {}
interface User {}
function useUserData(): User {}
interface Props {
data: User;
};
function UserInfo({ data }: Props) {}
ReturnType. Главное знать о его существовании и сами найдете применение в своем проекте)
Привет 👋
Задача, снова задача) Через тренировки можно подготовиться к большинству собеседований.
Условие:
Нужно дописать функцию, которая принимает два параметра:
1. Массив чисел
2. Функцию-предикат, которая принимает значение элемента массива и возвращает true или false.
Нужно найти в массиве индекс элемента, для которого предикат вернет true. Если такого элемента нет, вернуть -1.
Входные параметры:
findInArray([1, 3, 5, 6, 7], (value) => value % 2 === 0); // Должно вернуть 3
findInArray([1, 3, 5, 7], (value) => value % 2 === 0); // Должно вернуть -1
const findInArray = (arr, predicate) => arr.findIndex(predicate);
for, но в JS уже есть встроенные методы о которых нужно знать и использовать.
Инструмент недели — CSS Loaders
Мы часто сталкиваемся с проблемой отображения состояния загрузки. На CSS Loaders собраны готовые анимации лоадеров и все они на чистом CSS
Посмотреть можно тут: css-loaders.com
#css
Сегодня разбираем задачу с собеседования про цепочку промисов.
Промисы — это база для любого собеседования. На первый взгляд легко, но многие путаются из-за спешки или невнимательности.
Что выведет этот код в консоль?
Promise.resolve(123)
.then((x) => x + 1)
.catch((x) => x + 2)
.then((x) => x + 3)
.then((x) => console.log(x));
Promise.resolve(123) — промис сразу выполняется со значением 123..then((x) => x + 1) — прибавляем 1 к 123..catch((x) => x + 2) — пропускается, так как ошибки не было..then((x) => x + 3) — прибавляем 3 к 124..then(console.log) — выводим в консоль 127.catch срабатывает только при необработанной ошибке. В нашем случае ошибок нет, значит пропускаем.
Хочу попросить ваш фидбек — кажется, активность немного снизилась. Чего-то не хватает или может не заходит формат?
Поделитесь мыслями, я обязательно учту все комментарии (как минимум, запишу в свой блокнот) 🤔
Закончили с шутками и возвращаемся к привычным постам 🤗
Знали ли вы, что в CSS можно задавать переменные с конкретным типом через @property?
Теперь переменные могут быть не просто строками, а, например, числами, цветами или процентами. Это позволяет браузеру правильно интерпретировать их и использовать без ошибок.
Как объявить типизированную переменную?
Допустим, мы хотим создать переменную для управления размером элемента.
Раньше:
:root {
--size: 50;
}
--size — это число:
@property --size {
syntax: "<number>";
inherits: false;
initial-value: 10;
}
--size можно безопасно использовать в calc, transform и других свойствах.
@property --scale {
syntax: "<number>";
inherits: false;
initial-value: 1;
}
.box {
--scale: 1;
transform: scale(var(--scale));
transition: --scale 0.5s ease;
}
.box:hover {
--scale: 1.2;
}
@property.
Привет! Начнем неделю с задачи про контекст. Очень распростроенная задача
Что будет выведено в консоль?
for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
setTimeout выполняются после завершения цикла.i общая для всех итераций, и к моменту выполнения становится равной 5.i, которая уже имеет конечное значение.let вместо varlet, имеют блочную область видимости. Это значит, что каждая итерация цикла будет иметь свою собственную переменную i.
for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}
i как параметр.
for (var i = 0; i < 5; i++) {
(function(currentI) {
setTimeout(function() {
console.log(currentI);
}, 0);
})(i);
}
setTimeoutsetTimeout может принимать дополнительные аргументы, которые будут переданы в callback.
for (var i = 0; i < 5; i++) {
setTimeout(function(currentI) {
console.log(currentI);
}, 0, i);
}
bindbind для привязки значения i к callback-функции.
for (var i = 0; i < 5; i++) {
setTimeout(console.log.bind(null, i), 0);
}
0, 1, 2, 3, 4.
Сегодня отдыхаем 🍻
Наверное, не только у меня была тяжелая неделя, и хочется чуть-чуть потупить. Уже входит в привычку тупить по пятницам 😬
Делюсь демкой, на которую как-то наткнулся. Анимация переключения показалась мне достаточно интересной.
А ещё напоминаю, что вы можете поделиться своими находками в комментариях. Возможно, кто-то сам делает что-то подобное)
Ссылка на демо: CodePen
#CSS #animation
Продолжаем разбирать утилити-типы TypeScript. Сегодня на очереди Readonly<T>.
Этот тип делает все свойства объекта неизменяемыми. После создания объекта с Readonly, его свойства нельзя изменить, что помогает защитить данные от случайных изменений.
Зачем нужен Readonly?
1. Защита данных — предотвращает изменения объекта, если этого не должно происходить.
2. Предсказуемость кода — помогает избежать неожиданных багов, когда данные случайно изменяются.
3. Работа с неизменяемыми структурами данных — полезно для Redux, конфигурационных объектов и API-ответов.
Пример использования:
interface User {
name: string;
age: number;
};
type ReadonlyUser = Readonly<User>;
type ReadonlyUser = {
readonly name: string;
readonly age: number;
};
const user: ReadonlyUser = { name: "Alice", age: 25 };
user.age = 26; // Cannot assign to age because it is a read-only property.
Readonly стоит в ситуациях, когда объект не должен модифицироваться после создания: для конфигурационных данных, API-ответов, неизменяемых структур и управления состоянием в приложении.
Привет! Давайте начнем эту неделю с необычной для понедельника темы — поговорим о валидации форм в React.
Валидация данных — важная часть разработки. Можно проверять данные вручную, но это неудобно и приводит к громоздкому коду. Zod позволяет описывать структуры данных декларативно и сразу проверять их корректность.
Пример
import { z } from "zod";
const userSchema = z.object({
name: z.string(),
age: z.number().min(18),
});
const result = userSchema.safeParse({ name: "Антон", age: 42 });
if (!result.success) {
console.log(result.error.format()); // Ошибка: возраст должен быть 18+
}
type User = z.infer<typeof userSchema>;
User — это автоматически сгенерированный тип, который всегда соответствует схеме.
import { useForm } from "react-hook-form";
import { z } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";
const formSchema = z.object({
email: z.string().email(),
password: z.string().min(6, "Пароль должен содержать минимум 6 символов"),
});
type FormData = z.infer<typeof formSchema>;
export function LoginForm() {
const {
register,
handleSubmit,
formState: { errors },
} = useForm<FormData>({
resolver: zodResolver(formSchema),
});
const onSubmit = (data: FormData) => {
console.log("Успешный ввод:", data);
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
<div>
<label>Email:</label>
<input {...register("email")} />
{errors.email && <p>{errors.email.message}</p>}
</div>
<div>
<label>Пароль:</label>
<input type="password" {...register("password")} />
{errors.password && <p>{errors.password.message}</p>}
</div>
<button type="submit">Войти</button>
</form>
);
}
Инструмент недели — CSS Animation Generator.
Этот сайт позволяет легко и наглядно создавать анимации с помощью удобного интерфейса.
Настраиваете ключевые кадры, длительность, задержку и тип анимации, а генератор сразу выдаёт готовый CSS или JS код.
Попробовать можно тут: angrytools
#css
Как оставаться в тренде и не выгорать?
Фронтенд — это та сфера, где чуть ли не каждую неделю появляется что-то новое. Легко потеряться в бесконечном потоке фреймворков и библиотек. Вот несколько основных принципов, которые помогут прокачиваться и не выгореть:
1. База решает
Освой фундамент: HTML, CSS, JavaScript. Понимание основ позволит легко переключаться между фреймворками и библиотеками.
2. Не гнаться за хайпом
Каждый год выходит "новый убийца реакта"(и не только), но это не значит, что нужно сразу переписывать проект. Оцени реальную пользу технологий и их востребованность на рынке.
3. Практика через пет-проекты
Изучил новую технологию? Закрепи её на практике. Создай небольшое приложение или контрибуть в open-source. Это даст реальный опыт, а не просто "прочитанную статью".
4. Окружение решает
Ты уже сделал шаг в правильном направлении, подписавшись на мой канал 😉 А ещё можно вступить в наш чат(тык), чтобы общаться с единомышленниками, делиться опытом и задавать вопросы. Плюс, не забывай участвовать в митапах и конференциях — это отличный способ узнать о новинках и найти менторов.
5. Баланс между кодом и отдыхом
Регулярные перерывы и хобби вне программирования помогут избежать выгорания.
#career #Продуктивность #BestPractices
Предлагаю сегодня отдохнуть и немного похихикать.
Может у кого-то есть совпадения?)
А вы знали про CSS свойство caret-color?
Что делает caret-color?
Это свойство меняет цвет мигающего курсора в текстовых полях (<input>, <textarea>).
Курсор будет зеленого цвета:
input {
caret-color: lime;
}
caret-color: transparent; скрывает курсор совсем. @keyframes.
Привет! Продолжаем 🧑💻
Ранее мы уже обсуждали(тык) метод call, который позволяет управлять контекстом исполнения функции. Теперь разберём метод apply. Они похожи, но есть одно ключевое отличие: apply передаёт аргументы не по отдельности, а в виде массива.
Синтаксис:
functionName.apply(thisArg, [arg1, arg2, ...]);
thisArg — объект, который будет использоваться в качестве this.[arg1, arg2, ...] — массив аргументов, передаваемых в функцию.
function greet(city, country) {
console.log(`Привет, ${this.name} из города ${city}, ${country}!`);
}
const person = { name: 'Анатолий' };
// Передаём массив аргументов
greet.apply(person, ['Москва', 'Россия']);
// Привет, Анатолий из города Москва, Россия!
apply устанавливает this в контексте объекта person, а аргументы передаются массивом.call и applythis, но передача аргументов отличается:
greet.call(person, 'Москва', 'Россия'); // аргументы передаются отдельно
greet.apply(person, ['Москва', 'Россия']); // аргументы передаются массивом
apply — это способ передавать this и массив аргументов в функцию. В отличие от call, он подходит для ситуаций, когда данные уже собраны в массиве или заранее не известно количество аргументов.
Читать полностью…
Сегодня база про cookie. Мы работаем с ними каждый день, и про них спрашивают почти на каждом собеседовании.
Что такое cookie?
Cookie — это строка с данными, которую браузер сохраняет и автоматически отправляет серверу при каждом HTTP-запросе.
Как устанавливаются куки?
document.cookie = "token=abc123; path=/; max-age=3600";
Set-Cookie. Когда сервер хочет установить куку на клиенте, он отправляет этот заголовок в ответе на запрос.path: Определяет путь, для которого кука будет доступна. По умолчанию: / — доступна на всем сайте.expires: Устанавливает дату истечения срока действия куки. По умолчанию: кука сессионная и удаляется при закрытии браузера.max-age: Устанавливает время жизни куки в секундах. По умолчанию: не указан, кука сессионная.secure: Указывает, что кука будет передаваться только по HTTPS. По умолчанию: не установлен, кука будет передаваться как по HTTP, так и по HTTPS.samesite: Определяет, можно ли отправлять куку с запросами на другие сайты. По умолчанию: не указан, кука отправляется с запросами как с того же сайта, так и с внешних источников.httpOnly: Указывает, что кука будет доступна только для сервера и не может быть прочитана через JavaScript. Это улучшает безопасность, предотвращая атаки. По умолчанию: не установлен.max-age равным 0 или отрицательным числом.
Использовали хук useDeferredValue? Это хук, который помогает оптимизировать производительность, откладывая обновление менее важных частей интерфейса.
Что за хук?useDeferredValue — это хук из React 18, который позволяет "откладывать" обновление части интерфейса. Он принимает значение и возвращает его "отложенную" версию. Это значение обновляется только после того, как React завершит рендеринг более приоритетных задач.
Пример использования
Например, у нас есть компонент с инпутом для поиска и большим списком элементов. Без оптимизации каждое изменение в инпуте будет вызывать перерисовку всего списка, что может замедлить интерфейс.
import { useState, useDeferredValue } from "react";
function App() {
const [query, setQuery] = useState("");
const deferredQuery = useDeferredValue(query);
return (
<div>
<input
type="text"
value={query}
onChange={(e) => setQuery(e.target.value)}
placeholder="Введите текст"
/>
<List query={deferredQuery} />
</div>
);
}
function List({ query }) {
const items = Array.from({ length: 1000 }, (_, i) => `Item ${i}`);
const filteredItems = items.filter((item) =>
item.toLowerCase().includes(query.toLowerCase())
);
return (
<ul>
{filteredItems.map((item, index) => (
<li key={index}>{item}</li>
))}
</ul>
);
}
query обновляется сразу, но deferredQuery обновляется с задержкой.List использует deferredQuery, чтобы отфильтровать элементы. Благодаря этому:
Начинаем с поста о малоизвестном свойстве у input — valueAsNumber. Оно позволяет работать с числами напрямую, без ручного преобразования типов.
Что это за свойство?
Когда вы используете <input type="number">, значение по умолчанию возвращается как строка:
const value = input.value;
console.log(typeof value); // "string"
Number(value) или parseFloat(value).valueAsNumber:
const numberValue = input.valueAsNumber;
console.log(typeof numberValue); // "number"
valueAsNumber вернёт NaN. Это нужно учитывать при работе с данными:
if (!Number.isNaN(numberValue)) {
// значение можно использовать
} else {
// обработка ошибки
}
Сегодня поговорим о еще одном полезном утилити-типе в TypeScript — Record<Keys, Type>. Этот тип позволяет создавать объекты с определенными ключами и значениями
Что такое Record?Record<Keys, Type> — это утилита, которая создает объектный тип, где:
- Keys — набор ключей (обычно строковые литералы или объединение строк).
- Type — тип значений, которые будут связаны с этими ключами.
Проще говоря, Record позволяет задать фиксированный набор ключей и указать, какого типа должны быть их значения.
Пример использования
Предположим, у нас есть список цветов, и мы хотим связать каждый цвет с его HEX-кодом:
type Colors = 'red' | 'green' | 'blue';
type ColorHex = Record<Colors, string>;
const colorCodes: ColorHex = {
red: '#FF0000',
green: '#00FF00',
blue: '#0000FF',
};
Colors, который является объединением строковых литералов ('red', 'green', 'blue').Record, чтобы создать объект, где каждому ключу из Colors соответствует строковое значение (HEX-код).Colors присутствуют в объекте.string.Record, когда вам нужно описать объект с заранее известными ключами и типами значений.
⚡️React становится платным!⚡️
Meta официально объявила, что с 1 апреля 2025 года React переходит на подписку!
Теперь за использование библиотеки придётся платить $9.99/мес для разработчиков и $199/мес для компаний.
Основные изменения:
1️⃣ Бесплатной останется только React 17, но без обновлений.
2️⃣ В React 18+ появится DRM-защита – проект не соберется без лицензионного ключа.
3️⃣ В консоли будет всплывать огромный красный баннер: ⚠️ "React is unlicensed. Please purchase a subscription."
4️⃣ Удалены оптимизации (useMemo, useCallback, React.memo).
Кряк платной версии
На GitHub слили react18-crack.js, который:
✅ Возвращает вырезанный функционал для оптимизации.
✅ Подставляет лицензионный ключ REACT_ENTERPRISE_EDITION=TRUE
✅ Отключает баннер "React is unlicensed" в консоли.
Что делать без кряка?
1️⃣Платить за подписку.
2️⃣ Пересаживаться на Vue.
3️⃣Переписывать всё на jQuery.
#react
Может быть, кто-то еще не в курсе, но у нас есть чат, где вы можете общаться с другими разработчиками.
К сожалению, я не всегда могу найти время, чтобы участвовать в обсуждениях, но очень радует, что другие ребята активно поддерживают, отвечают на вопросы и делятся опытом. Спасибо всем за участие ❤️
Подписывайтесь @TrueFrontenderChat ☺️
А я ухожу на выходные отдыхать и готовить для вас новые посты)
Всем хороших выходных, встретимся в понедельник!
P.S. Не забывайте ставить реакции. Вам не сложно, мне приятно 🥰
Знали ли вы о свойстве text-decoration-skip-ink?
Это свойство, которое управляет поведением, когда линия проходит через часть символа или глифа. Это даёт контроль над тем, как подчеркивания взаимодействуют с символами.
Как это работает?
По умолчанию браузеры пропускают области, где символы пересекают линию подчеркивания, что делает текст более читаемым и эстетичным.
Есть 2(3) значения:
1. Когда установлено auto, браузер сам решает, где пропустить линию подчеркивания, чтобы улучшить читаемость текста. Например, подчеркивание будет пропущено через те части символов, которые нарушают его целостность.
a {
text-decoration: underline;
text-decoration-skip-ink: auto; /* Браузер сам решает, где пропустить линию */
}
none, то линия подчеркивания будет проходить через все символы, независимо от того, пересекает ли она их соединительные элементы. Это может быть полезно, если вы хотите, чтобы подчеркивание было единообразным и охватывало весь текст.
a {
text-decoration: underline;
text-decoration-skip-ink: none; /* Линия будет проходить через все символы */
}
Сегодня у нас снова задача с реального собеседования. На этот раз — про рекурсию.
Разберем задачу, которую довольно часто спрашивают на позиции джуна/мидла.
Что такое рекурсия?
Рекурсия — это функция, которая вызывает саму себя для решения задачи. Каждый вызов функции работает с частью задачи, пока не достигается базовый случай.
Одна из популярных задач — написать функцию sum, которая позволяет складывать числа последовательно:
sum(1)(2)(3)(); // 6
sum(5)(-1)(2)(3)(); // 9
sum(10)(20)(30)(40)(50)(); // 150
function sum(a) {
return (b) => b === undefined ? a : sum(a + b);
}
sum(1)" возвращает функцию с замыканием, где a = 1.(2)" накапливает сумму: 1 + 2 = 3.(3)" увеличивает сумму: 3 + 3 = 6.()" условие b === undefined становится истинным, и возвращается результат — 6.sum использует рекурсию и замыкание, чтобы накапливать сумму и возвращать результат при вызове без аргументов.
Предлагаю сегодня снова немного расслабиться перед выходными 🤗
А вы часто копируете чужой код? Или, может быть, пользуетесь ИИ?
Разберем одну из типичных задач с собеседования про мемоизацию.
Что такое мемоизация?
Мемоизация — это способ оптимизации, который позволяет хранить результаты вызова функции с определенными параметрами и повторно использовать их при одинаковых входных данных.
Задача:
Напишите функцию memoize, которая принимает одну функцию и возвращает новую функцию, мемоизирующую её вызовы.
Решение:
function memoize(fn) {
const cache = {}; // Для хранения кэша
return function(...args) {
const key = JSON.stringify(args); // Уникальный ключ для аргументов
if (key in cache) {
return cache[key]; // Возвращаем кэшированный результат
}
const result = fn(...args); // Если нет в кэше — вычисляем результат
cache[key] = result; // Кэшируем результат
return result;
};
}
// Пример функции, для которой применяется мемоизация
const slowAdd = (a, b) => a + b;
const memoizedAdd = memoize(slowAdd);
console.log(memoizedAdd(1, 2)); // Будет вычислено и сохранено в кэш
console.log(memoizedAdd(1, 2)); // Результат взят из кэша, выполнено быстрее
memoize, которая принимает любую функцию.
Привет! Начнем эту неделю с TypeScript 🖼️
Когда-то давно мы уже разбирали утилити-тип Pick, а сегодня познакомимся с его антиподом — Omit.Omit<T, K> позволяет исключить определённые свойства из типа.
Как он работает?T — исходный тип.K — ключи, которые нужно исключить. Они указываются в виде строки или объединения строк. Например:
— Для одного свойства: Omit<T, 'key'>.
— Для нескольких свойств: Omit<T, 'key1' | 'key2'>.
Пример:
type User = {
id: number;
name: string;
email: string;
age: number;
};
type UserContactInfo = Omit<User, 'id' | 'age'>;
type UserContactInfo = {
name: string;
email: string;
};
name и age.Pick можно почитать в этом посте(тык).
Сегодня у нас кастомный хук — useLatest. Он помогает избежать проблем с устаревшими данными в замыкании.
В чём проблема?
Когда передаём состояние в асинхронную функцию, оно остаётся таким, каким было на момент создания функции. В результате в setTimeout, setInterval или обработчиках событий получаем устаревшие значения.
const [count, setCount] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
console.log(count); // Всегда 0
}, 1000);
return () => clearInterval(interval);
}, []);
function useLatest(value) {
const ref = useRef(value);
useEffect(() => {
ref.current = value;
}, [value]);
return ref;
}
useEffect:
const [count, setCount] = useState(0);
const countRef = useLatest(count);
useEffect(() => {
const interval = setInterval(() => {
console.log(countRef.current); // Всегда актуальное значение count
}, 1000);
return () => clearInterval(interval);
}, []);
useLatest инкапсулирует всю логику в одном месте, что делает код чище и надёжнее.
Джун? Проходишь собеседования? Вот одна из задач, которая может встретиться на собесе.
Задача:
Написать функцию, которая принимает бесконечное количество аргументов и возвращает массив, где каждое число умножено на 2.
Пример:
multiplyByTwo(1, 2, 3, 5, 10, 22);
// [2, 4, 6, 10, 20, 44]
const multiplyByTwo = (...rest) => {
return rest.map(item => item * 2);
};
...rest собирает переданные аргументы в массив. map() проходит по каждому элементу и умножает его на 2. call
function multiplyByTwo() {
return Array.prototype.map.call(arguments, item => item * 2);
}
multiplyByTwo, arguments содержит все переданные аргументы. map через call с контекстом arguments, чтобы применить метод map к этому псевдомассиву. map проходит по каждому элементу и умножает его на 2.
Сегодня особенный день, и хочется сказать пару слов о том, как девушки делают фронтенд (и не только) лучше 😘
— Как хороший UI, девушки делают мир вокруг эстетичнее.
— Никто так не умеет решать 10 задач одновременно, как они.
— В команде всегда есть та, кто вытянет проект на себе, даже если всё горит.
— Девушки привносят в индустрию креативность и нестандартные решения.
— Как бы ни менялись технологии и задачи, они всегда находят лучшее решение.
Коллеги, цените девушек и поздравьте их сегодня от души 🌹