Сборная солянка про фронтенд. JavaScript, React, TypeScript, HTML, CSS — здесь обсуждаем всё, что связано с веб-разработкой! Связь: @pmowq
Использовали хук 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 задач одновременно, как они.
— В команде всегда есть та, кто вытянет проект на себе, даже если всё горит.
— Девушки привносят в индустрию креативность и нестандартные решения.
— Как бы ни менялись технологии и задачи, они всегда находят лучшее решение.
Коллеги, цените девушек и поздравьте их сегодня от души 🌹
Йопта, братва 😎 Завозим нормальный язык для нормальных пацанов. Хватит дрочиться с этим вашим JavaScript, вот вам ЙоптаScript — код, который понимает каждый уважающий себя разработчик.
Как эта дичь работает?
ЙоптаScript — это переводчик с понятного языка на этот ваш стандартный JS. Пишешь по-пацански — он делает красиво.
ясенХуй бабки внатуре 1000 нахуй
вилкойвглаз (бабки поцик 500) жЫ
красноглазое.чмо("Красавчик, бабки есть, гуляем!") нахуй
есть иливжопураз жЫ
красноглазое.чмо("Опять доширак... ну и похер.") нахуй
есть
const бабки = 1000;
if (бабки >= 500) {
console.log("Красавчик, бабки есть, гуляем!");
} else {
console.log("Опять доширак... ну и похер.");
}
ясенХуй
— объявляем переменную, как const
.вилкойвглаз
— это if
, проверяем расклад.поцик
— >=
, типа, бабки либо есть, либо сосем лапу.красноглазое.чмо()
— console.log()
, но по-пацански.иливжопураз
— else
, если не фартануло.Инструмент недели — 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
вместо var
let
, имеют блочную область видимости. Это значит, что каждая итерация цикла будет иметь свою собственную переменную 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);
}
setTimeout
setTimeout
может принимать дополнительные аргументы, которые будут переданы в callback.
for (var i = 0; i < 5; i++) {
setTimeout(function(currentI) {
console.log(currentI);
}, 0, i);
}
bind
bind
для привязки значения 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
и apply
this
, но передача аргументов отличается:
greet.call(person, 'Москва', 'Россия'); // аргументы передаются отдельно
greet.apply(person, ['Москва', 'Россия']); // аргументы передаются массивом
apply
— это способ передавать this
и массив аргументов в функцию. В отличие от call
, он подходит для ситуаций, когда данные уже собраны в массиве или заранее не известно количество аргументов.
Читать полностью…
Рабочая неделя подошла к концу 🥳 Надеюсь, вы успели закрыть все задачи и готовы к выходным 🍻
Всем хорошей пятницы и выходных! Берегите себя и не забывайте отдыхать 🫶
Кто-нибудь использовал псевдокласс :focus-within
? Сегодня короткий пост о том, как он работает.
Что делает focus-within?
Этот селектор срабатывает, если элемент или любой его потомок находится в фокусе.
Пример
Простой пример, в котором форма плавно увеличивается при фокусе на любой дочерний фокусируемый элемент: CodePen
/* Стили применятся к форме, когда любой её дочерний элемент будет в фокусе */
.form:focus-within {
border-color: dodgerblue;
box-shadow: 0 0 15px rgba(30, 144, 255, 0.5);
background: #ffffff;
transform: scale(1.05);
}
:focus-within
работает со всеми фокусируемыми элементами.