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

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

Связь: @pmowq
Download Telegram
Привет! Давайте начнем эту неделю с необычной для понедельника темы — поговорим о валидации форм в 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+
}


Zod + TypeScript

type User = z.infer<typeof userSchema>;


User — это автоматически сгенерированный тип, который всегда соответствует схеме.

Простая валидация формы
Используем Zod вместе с React Hook Form, чтобы валидировать форму без лишнего кода.

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>
);
}


Почему Zod?
- Чистый синтаксис – описание схем читается легко
- Работает с TypeScript – выводит типы автоматически
- Без внешних зависимостей
- Можно использовать в API, формах, конфигурациях

Итог
Zod — это отличный инструмент, который упрощает валидацию данных. Он избавляет от громоздких проверок вручную, отлично интегрируется с TypeScript и React Hook Form и тд

#react #OpenSource #typescript
👏9👍8🔥6
Сегодня у нас снова задача с реального собеседования. На этот раз — про рекурсию.
Разберем задачу, которую довольно часто спрашивают на позиции джуна/мидла.

Что такое рекурсия?
Рекурсия — это функция, которая вызывает саму себя для решения задачи. Каждый вызов функции работает с частью задачи, пока не достигается базовый случай.

Одна из популярных задач — написать функцию 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 использует рекурсию и замыкание, чтобы накапливать сумму и возвращать результат при вызове без аргументов.

Небольшое уточнение:
Это не классическая рекурсия, а комбинация замыканий и каррирования. Функция накапливает сумму через замыкания и завершает вычисление при вызове без аргумента.

#interview #JavaScript
👍11👀3
Продолжаем разбирать утилити-типы 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;
};


Теперь при попытке изменить свойства такого объекта TypeScript выдаст ошибку:

const user: ReadonlyUser = { name: "Alice", age: 25 };
user.age = 26; // Cannot assign to age because it is a read-only property.


Использовать Readonly стоит в ситуациях, когда объект не должен модифицироваться после создания: для конфигурационных данных, API-ответов, неизменяемых структур и управления состоянием в приложении.

#typescript #interview
👍9🔥3👀1
Знали ли вы о свойстве text-decoration-skip-ink?
Это свойство, которое управляет поведением, когда линия проходит через часть символа или глифа. Это даёт контроль над тем, как подчеркивания взаимодействуют с символами.

Как это работает?
По умолчанию браузеры пропускают области, где символы пересекают линию подчеркивания, что делает текст более читаемым и эстетичным.

Есть 2(3) значения:
1. Когда установлено auto, браузер сам решает, где пропустить линию подчеркивания, чтобы улучшить читаемость текста. Например, подчеркивание будет пропущено через те части символов, которые нарушают его целостность.


a {
text-decoration: underline;
text-decoration-skip-ink: auto; /* Браузер сам решает, где пропустить линию */
}


2. Если значение установлено на none, то линия подчеркивания будет проходить через все символы, независимо от того, пересекает ли она их соединительные элементы. Это может быть полезно, если вы хотите, чтобы подчеркивание было единообразным и охватывало весь текст.


a {
text-decoration: underline;
text-decoration-skip-ink: none; /* Линия будет проходить через все символы */
}


Это свойство поддерживается в современных браузерах, кроме IE.

#CSS
🔥9👍42
This media is not supported in your browser
VIEW IN TELEGRAM
Сегодня отдыхаем 🍻
Наверное, не только у меня была тяжелая неделя, и хочется чуть-чуть потупить. Уже входит в привычку тупить по пятницам 😬

Делюсь демкой, на которую как-то наткнулся. Анимация переключения показалась мне достаточно интересной.
А ещё напоминаю, что вы можете поделиться своими находками в комментариях. Возможно, кто-то сам делает что-то подобное)

Ссылка на демо: CodePen

#CSS #animation
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥932
Может быть, кто-то еще не в курсе, но у нас есть чат, где вы можете общаться с другими разработчиками.

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

Подписывайтесь @TrueFrontenderChat ☺️

А я ухожу на выходные отдыхать и готовить для вас новые посты)
Всем хороших выходных, встретимся в понедельник!

P.S. Не забывайте ставить реакции. Вам не сложно, мне приятно 🥰
Please open Telegram to view this post
VIEW IN TELEGRAM
125👾2👍1
Привет! Начнем неделю с задачи про контекст. Очень распростроенная задача

Что будет выведено в консоль?

for (var i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}


Ответ:
5, 5, 5, 5, 5

Почему так происходит?
- Колбеки setTimeout выполняются после завершения цикла.
- Переменная i общая для всех итераций, и к моменту выполнения становится равной 5.
- Все колбеки ссылаются на одну переменную i, которая уже имеет конечное значение.

Как исправить?
1. Использование let вместо var
Переменные, объявленные через let, имеют блочную область видимости. Это значит, что каждая итерация цикла будет иметь свою собственную переменную i.


for (let i = 0; i < 5; i++) {
setTimeout(function() {
console.log(i);
}, 0);
}


2. Использование IIFE
Мы можем создать новую область видимости для каждой итерации с помощью IIFE. Анонимная функция вызывается сразу же, передавая текущее значение i как параметр.

for (var i = 0; i < 5; i++) {
(function(currentI) {
setTimeout(function() {
console.log(currentI);
}, 0);
})(i);
}


3. Использование дополнительного параметра в setTimeout
Функция setTimeout может принимать дополнительные аргументы, которые будут переданы в callback.

for (var i = 0; i < 5; i++) {
setTimeout(function(currentI) {
console.log(currentI);
}, 0, i);
}


4. Использование bind
Можно использовать метод bind для привязки значения i к callback-функции.


for (var i = 0; i < 5; i++) {
setTimeout(console.log.bind(null, i), 0);
}


Во всех этих вариантах результат будет: 0, 1, 2, 3, 4.


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

#JavaScript #interview
👍13🔥3
⚡️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
Please open Telegram to view this post
VIEW IN TELEGRAM
😁174🤯2🦄21👀1👾1
Закончили с шутками и возвращаемся к привычным постам 🤗

Знали ли вы, что в 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.

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

#css
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥7👍5
Сегодня поговорим о еще одном полезном утилити-типе в 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-код).

Теперь TypeScript гарантирует, что:
- Все ключи из Colors присутствуют в объекте.
- Значения имеют тип string.

Используйте Record, когда вам нужно описать объект с заранее известными ключами и типами значений.

#typescript
👍92🔥1
Хочу попросить ваш фидбек — кажется, активность немного снизилась. Чего-то не хватает или может не заходит формат?

Поделитесь мыслями, я обязательно учту все комментарии (как минимум, запишу в свой блокнот) 🤔
Please open Telegram to view this post
VIEW IN TELEGRAM
112🔥1
Начинаем с поста о малоизвестном свойстве у 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 {
// обработка ошибки
}

Про работу с числами я писал в этом(тык) посте.

Когда стоит использовать:
- При работе с числовыми значениями из форм
- Чтобы избежать лишнего преобразования типов
- Для более чистого кода

#JavaScript
🔥12👍83👏1👀1
Сегодня разбираем задачу с собеседования про цепочку промисов.
Промисы — это база для любого собеседования. На первый взгляд легко, но многие путаются из-за спешки или невнимательности.

Что выведет этот код в консоль?

Promise.resolve(123)
.then((x) => x + 1)
.catch((x) => x + 2)
.then((x) => x + 3)
.then((x) => console.log(x));


Ответ: 127

Объяснение:
1. Promise.resolve(123) — промис сразу выполняется со значением 123.
2. .then((x) => x + 1) — прибавляем 1 к 123.
3. .catch((x) => x + 2) — пропускается, так как ошибки не было.
4. .then((x) => x + 3) — прибавляем 3 к 124.
5. .then(console.log) — выводим в консоль 127.

Важно:
catch срабатывает только при необработанной ошибке. В нашем случае ошибок нет, значит пропускаем.

#interview #JavaScript
🔥10👍61
Использовали хук 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>
);
}


Как это работает?
1. Пользователь вводит текст в инпут.
2. Состояние query обновляется сразу, но deferredQuery обновляется с задержкой.
3. Компонент List использует deferredQuery, чтобы отфильтровать элементы. Благодаря этому:
— Инпут не зависает.
— Фильтрация списка происходит чуть позже, когда React "освободится".

Когда использовать?
— Если у вас есть большие списки или сложные вычисления , которые могут замедлить интерфейс.
— Если нужно отложить обновление части UI, чтобы не блокировать основной поток.

#react #BestPractices
👍6🔥2
This media is not supported in your browser
VIEW IN TELEGRAM
Инструмент недели — CSS Loaders

Мы часто сталкиваемся с проблемой отображения состояния загрузки. На CSS Loaders собраны готовые анимации лоадеров и все они на чистом CSS

Посмотреть можно тут: css-loaders.com

#css
🔥9👍62
Подборка пятничных мемов 🌈
Please open Telegram to view this post
VIEW IN TELEGRAM
Please open Telegram to view this post
VIEW IN TELEGRAM
🔥12😁8👍4