React Context не вызывает лишние рендеры

09.05.2025, Ссылка на статью

Очередная статья про React Context и объяснение того, как с ним работать верно для обработки состояния. Статья поясняет, что разработчики предпочитают стейт-менеджеры, например Redux или Zustand вместо того, чтобы разобраться с контекстами.

В базовом примере автор статьи использует контекст для передачи state и setState через контекст без каких либо мемоизаций, и уже в таком сценарии Реакт не делает лишние рендеры компонентов, которые не были затронуты изменением состояния:

const ValueContext = createContext();
 
const Provider = ({ children }) => {
  const [value, setValue] = useState(0);
 
  return (
    <ValueContext.Provider value={{ value, setValue }}>
      {children}
    </ValueContext.Provider>
  );
};

Использование такого контекста позволяет проверить корректность его работы – лишних рендеров не будет. Для примере можно собрать такую же структуру как и в статье:

Пример иерархии компонентов приложения с React.Context

Для такого примера App и UncontrolledComponent не будут рендерится, т.к. не используют Context Consumer.

Компоненты, затронутые изменением React.Context

Автор считает, что путаница возникает из-за того, что в один контекст складывают все возможные состояния.

Совет по оптимизации

Если передавать дочерние элементы через пропы, например через {children}, то они не будут рендерится при изменении состояния компонента. Данный подход хорошо себя показывает вкупе с React.Context.

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

Ссылка на оригинал

DI в React, но не тот, о котором подумали

09.05.2025, Ссылка на статью

В мире разработки на React’е мы очень часто пишем компоненты, которые тесно связаны, но потом обнаруживаем, что такие компоненты тяжело тестировать, поддерживать и изменять.

Статья предлагает рассматривать инверсию зависимостей, чтобы избавиться от таких проблем, как:

  • тесная связь кода с API-слоём;
  • сложность тестирования вызовов API;
  • трудности в изменении источника данных;
  • сложности с пониманием состояния запросов и т.д.

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

interface UserRepository {
	getUser: () => Promise<User>;
}
 
class ApiUserRepository implements UserRepository {
	async getUser(): Promise<User> {
		const response = await fetch('/api/user');
		return response.json();
	}
}
 
class MockUserRepository implements UserRepository {
	private userPromiseWithResolvers?: PromiseWithResolvers<User>;
 
	getUser(): Promise<User> {
		this.userPromiseWithResolvers = Promise.withResolvers();
		return this.userPromiseWithResolvers.promise;
	}
 
	// Метод для резолва промиса
	resolveWithUser(user: User) {
		this.userPromiseWithResolvers?.resolve(user);
	}
 
	// Метод для реджекта промиса
	rejectUser(error: Error) {
		this.userPromiseWithResolvers?.reject(error);
	}
}

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

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

Из статьи можно вынести лучшие практики:

  • необходимо определять четкие интерфейсы, представляющие зависимости в проекте;
  • использовать экземпляры интерфейсов через пропы или контекст (или через тот самый DI – TSyringe);
  • писать легко тестируемые компоненты с изолированными тестами.
Ссылка на оригинал

Rslib – и снова новый бандлер библиотек

14.05.2025, Ссылка на статью

Уже некоторая традиция – каждые несколько недель анонсировать новый бандлер. На этот раз новый бандлер – Rslib, разрабатываемый для экосистемы Rstack.

Экосистема Rstack

Главными фичами бандлера явлются поддержка экосистемы – легко заменить один бандлер на другой, поддержка вывода нескольких форматах, Bundle & Bundleless режимы и работа с различными ресурсами.

Отдельно Rslib отмечает хорошую работу с Module Federation с использование более гибкого варианта, чем в Wepback. Скорее всего Rslib использует Module Federation 2.0, добавляющий работу с типизацией, распределением зависимостей, поддержкой Manifest’ов и режиме в рантайме.

С каждым новым бандлером мы должны выбирать приоритетные фичи: лучше сборка и вес бандла, более лучшая поддержка TypeScript, или распределение на микрофронтеды.

Ссылка на оригинал

Chrome 137 Beta – нововведения в CSS

01.05.2025, Ссылка на новость

В бету Хромиума 137 добавили несколько крутых фич для CSS, и первая из них – if() в CSS! Функция if() – лёгкий способ на основе CSS-переменных описывать условные значения для свойств. Например можно простым способом описать переключение светлой и тёмной темы только через одну переменную:

:root {
	--dark-color: #232323;
	--light-color: #f5f5f5;
}
 
body {
	--color: var(--light-color);
 
	background-color: var(--color);
	color: if(
		style(--color: var(--light-color)): var(--dark-color);
		else: var(--light-color)
	);
  
	@media (prefers-color-scheme: dark) {
		--color: var(--dark-color);
	}
}

Функция if() на данный момент – сокращение синтаксиса для кастомных CSS-функций.

Вторая крутая фича – логические свойства для изменения порядка в a11y-дереве. Это полезно для тех случаев, когда порядок элементов изменен через order во Флексах, или через grid-area в Гридах. До 137 Хромиума доступность таких элементов страдала, т.к. порядок визуально был один, а через скринридеры – другой. Теперь мы сможем ещё лучше настраивать Accessibility.

Из приятных плюшек нам добавят более лучшую поддержку функции shape(), добавленной в Chrome 136, которая позволяет описать кастомный путь для обрезки контента, а со 137 версии эта функция станет доступна и для настройки отступов.

Ссылка на оригинал

CSS Contrast Color

13.05.2025, Ссылка на статью

Safari Technology Preview выкатил новую фичу – CSS-функцию contrast-color(), которая на основе переданного цвета возвращает либо черный, либо белый цвет. Данная функция может быть очень полезно для разработки специальных режимов отображения сайтов и её можно использовать в специальном медиа-запросе, который узнаёт, что пользователь предпочитает более контрастный интерфейс.

.button {
	--color: royalblue;
	--bg-color: rgb(from var(--color) r g b / 20%);
 
	color: var(--color);
	background-color: var(--bg-color);
	
	@media (prefers-contrast: more) {
		color: contrast-color(var(--color));
	}
}

Расчёт контрастного цвета

Контрастный цвет рассчитывается согласно спецификации WCAG 2.01, и в будущем ожидается, что в WCAG 3.0 будет добавлен улучшенный алгоритм расчёта контрастности цвета – Accessible Perceptual Contrast Algorithm (APCA).

Пример работы алгоритма контрастности APCA

В 2021 году в Safari Technology Preview 122 была добавлена более гибкая функция для работы с контрастными цветами, которая позволяла сравнить два цвета между собой и выбрать один из переданных цветов. Фича в итоге не была добавлена в браузеры, и её перенесли в CSS Color Module Level 6 и переименовали в contrast-color(). В будущем спецификация подразумевает, что мы сможем узнать контраст цвета с использованием определенного алгоритма и выбрать один из переданных цветов.

Сноски

  1. WCAG – Web Content Accessibility Guidelines.

Ссылка на оригинал

Explicit Resource Management – учимся автоматически выгружать неиспользуемые ресурсы

09.05.2025, Ссылка на новость

Resource Management Unlimited Power

В стандарте ECMAScript появится явное управление ресурсами – более явный подход для работы с дескрипторами файлов, соединениями и любыми объектами, реализующими [Symbol.dispose]() или [Symbol.asyncDispose](). Для удобства работы с такими ресурсами появятся новые ключевые слова – using и await using, которые были добавлены в TypeScript 5.2.

Ключевое слово using вызывает [Symbol.dispose]() синхронно при выходе из блока, для await using схожая логика, за тем отличием, что в [Symbol.asyncDispose]() можно написать асихнронную логику.

Дополнительно к ключевым словам и специальным символам были добавлены новые классы – DisposableStack и AsyncDisposableStack, которые необходимы для агрегации ресурсов. Классы для агрегации обеспечивают более широкий функционал и позволяют описать поведение для не-disposable ресурсов, добавить обработку в конце всех dispose и явно вызвать очистку ресурсов.

Новую фичу обсуждали на прошедшем meeting of Ecma TC39 и её перевод в Stage 4, а ещё она стала доступна с Хромиума 134.

Ссылка на оригинал