Использование Intersection Observer в React
react-intersection-observer – библиотека для использования Intersection Observer в React. Можно использовать через хук useInView и через компонент InView:
Поддерживаются параметры Intersection Observer v2: trackVisibility и delay.
В репозитории есть рецепты готовых сценариев использования: ленивая загрузка изображений, триггер анимации и отслеживание показов.
https://github.com/thebuilder/react-intersection-observer
react-intersection-observer – библиотека для использования Intersection Observer в React. Можно использовать через хук useInView и через компонент InView:
const Component = () => {
const { ref, inView, entry } = useInView({
/* Optional options */
threshold: 0,
});
return (
<div ref={ref}>
<h2>{`Header inside viewport ${inView}.`}</h2>
</div>
);
};
Поддерживаются параметры Intersection Observer v2: trackVisibility и delay.
В репозитории есть рецепты готовых сценариев использования: ленивая загрузка изображений, триггер анимации и отслеживание показов.
https://github.com/thebuilder/react-intersection-observer
GitHub
GitHub - thebuilder/react-intersection-observer: React implementation of the Intersection Observer API to tell you when an element…
React implementation of the Intersection Observer API to tell you when an element enters or leaves the viewport. - thebuilder/react-intersection-observer
👍18
Релиз Storybook 8 Beta
Вышел релиз Storybook 8 Beta. Какие он принес изменения:
- Значительное улучшение производительности. Поменяли компилятор, вместо Babel теперь SWC. Для проектов на React автоматически генерируются контролы с помощью react-docgen, это ускорило время запуска на 20%.
- Обновили UI на мобильных устройствах. Теперь на мобильных устройствах боковая панель будет появляться снизу.
- Улучшили утилиты для тестирования. Объединили
- Поддержка React Server Components. Пока что в историях можно использовать RSC только в Next.js проектах.
- Обновили версии основных зависимостей. Для Storybook 8 потребуется Node.js 18+, Vite 5 и Lit 3 версии.
https://storybook.js.org/blog/storybook-8-beta/
Вышел релиз Storybook 8 Beta. Какие он принес изменения:
- Значительное улучшение производительности. Поменяли компилятор, вместо Babel теперь SWC. Для проектов на React автоматически генерируются контролы с помощью react-docgen, это ускорило время запуска на 20%.
- Обновили UI на мобильных устройствах. Теперь на мобильных устройствах боковая панель будет появляться снизу.
- Улучшили утилиты для тестирования. Объединили
@storybook/jest и @storybook/testing-library в один модуль @storybook/test, который основан на Vitest. - Поддержка React Server Components. Пока что в историях можно использовать RSC только в Next.js проектах.
- Обновили версии основных зависимостей. Для Storybook 8 потребуется Node.js 18+, Vite 5 и Lit 3 версии.
https://storybook.js.org/blog/storybook-8-beta/
Storybook Blog
Storybook 8 Beta
Major compatibility & performance improvements
👍15👎1
LLRT – среда выполнения от AWS
LLRT – облегченная среда выполнения JS от AWS. LLRT обеспечивает в 10 раз более быстрый запуск приложения и в 2 раза более низкую общую стоимость по сравнению с другими средами, работающими в AWS Lambda. По времени запуска LLRT быстрее Node.js, Bun или Deno.
LLRT написан на Rust и использует QuickJS в качестве JS движка. Хоть LLRT и не позиционирует себя в качестве замены Node.js, его API частично совместимо с API Node.js.
На данный момент вместе с LLRT есть 14 известных мне сред выполнения JS:
- Node.js (V8)
- Deno (V8)
- Bun (JavaScript core - WebKit)
- Workerd (Cloudflare, V8)
- LLRT (AWS, V8)
- WinterJS (Wasmer, Spider Money)
- Edge Runtime (Vercel, V8)
- Lagon (Vercel Acquired, V8)
- Compute @edge (Fastly, Spider Monkey)
- Edge Compute (Alibaba V8)
- Hermes (Facebook)
- Bloomberg (v8, Internal)
- Bytedance (v8, Internal)
- txiki (QuickJS)
https://github.com/awslabs/llrt
LLRT – облегченная среда выполнения JS от AWS. LLRT обеспечивает в 10 раз более быстрый запуск приложения и в 2 раза более низкую общую стоимость по сравнению с другими средами, работающими в AWS Lambda. По времени запуска LLRT быстрее Node.js, Bun или Deno.
LLRT написан на Rust и использует QuickJS в качестве JS движка. Хоть LLRT и не позиционирует себя в качестве замены Node.js, его API частично совместимо с API Node.js.
На данный момент вместе с LLRT есть 14 известных мне сред выполнения JS:
- Node.js (V8)
- Deno (V8)
- Bun (JavaScript core - WebKit)
- Workerd (Cloudflare, V8)
- LLRT (AWS, V8)
- WinterJS (Wasmer, Spider Money)
- Edge Runtime (Vercel, V8)
- Lagon (Vercel Acquired, V8)
- Compute @edge (Fastly, Spider Monkey)
- Edge Compute (Alibaba V8)
- Hermes (Facebook)
- Bloomberg (v8, Internal)
- Bytedance (v8, Internal)
- txiki (QuickJS)
https://github.com/awslabs/llrt
GitHub
GitHub - awslabs/llrt: LLRT (Low Latency Runtime) is an experimental, lightweight JavaScript runtime designed to address the growing…
LLRT (Low Latency Runtime) is an experimental, lightweight JavaScript runtime designed to address the growing demand for fast and efficient Serverless applications. - awslabs/llrt
👍11🔥2❤1
Релиз Million.js 3
Вышел релиз Million.js 3. Подробнее что это написано здесь.
Какие изменения в новой версии:
- Улучшенная производительность. Для SSR приложений основной проблемой производительности была гидратация, ее сложность была O(n), где n – количество DOM узлов. В Million.js 3 уменьшили сложность гидратации до O(d), где d – количество динамических DOM узлов, d ≤ n:
- Стабильность. Переписали компилятор для лучшей поддержки TypeScript, множественных возвратов, условных операторов и для обработки вложенных React компонентов.
https://million.dev/blog/million-3
Вышел релиз Million.js 3. Подробнее что это написано здесь.
Какие изменения в новой версии:
- Улучшенная производительность. Для SSR приложений основной проблемой производительности была гидратация, ее сложность была O(n), где n – количество DOM узлов. В Million.js 3 уменьшили сложность гидратации до O(d), где d – количество динамических DOM узлов, d ≤ n:
// Million.js 3 гидратирует только handleClick и count
<div>
<h1>Hello, world!</h1>
<button onClick={handleClick}>{count}</button>
</div>
- Стабильность. Переписали компилятор для лучшей поддержки TypeScript, множественных возвратов, условных операторов и для обработки вложенных React компонентов.
https://million.dev/blog/million-3
Telegram
Заметки про React
Million.js: замена VDOM для React
Million.js – это замена VDOM для React, которая увеличивает скорость работы приложения в несколько раз. Библиотека использует подход Block Virtual DOM, который по другому определяет различия и его работу можно разбить на…
Million.js – это замена VDOM для React, которая увеличивает скорость работы приложения в несколько раз. Библиотека использует подход Block Virtual DOM, который по другому определяет различия и его работу можно разбить на…
👍7👎2
Над чем работают в React Labs
В блоге React вышел новый пост о статусе работы над текущими проектами. Кратко, о чем написали:
- React Compiler готовится к релизу и уже успешно используется в проде на некоторых проектах. Компилятор будет автоматически оптимизировать код приложения, чтобы изменение стейта внутри компонента не повлекло за собой лишние ре-рендеры дочерних компонентов.
Компилятор будет оптимизировать только те компоненты, которые подходят под определенные условия. Одно из таких условий – идемпотентность компонента, т.е. всегда возвращает одинаковый результат при одном и том же наборе пропсов.
- Actions. Раньше предполагалось делать Actions, которые выполняют код на сервере – Server Actions. Сейчас API расширили и он стал называться просто Actions, поддерживая выполнение действия и на клиенте:
Функцию в action можно будет определить как на сервере, используя директиву 'use server', так и на клиенте. При работе с action будут доступны хуки отслеживания состояния формы useFormStatus и useFormState.
- Директивы "use client" и "use server" будут определять «точки разделения» между окружениями. Директива "use client" будет сообщать бандлеру о необходимости генерации <noscript> (как в Astro Islands), а "use server" сообщит бандлеру о необходимости генерации POST endpoint (как в tRPC Mutations).
- Document Metadata. Появится встроенная поддержка для рендера <noscript>, <meta>, <link> в любом месте дерева компонентов, как в React Helmet.
- Загрузка ресурсов. Интеграция Suspense с жизненным циклом загрузки ресурсов, таких как стили, шрифты и скрипты.
https://react.dev/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024
В блоге React вышел новый пост о статусе работы над текущими проектами. Кратко, о чем написали:
- React Compiler готовится к релизу и уже успешно используется в проде на некоторых проектах. Компилятор будет автоматически оптимизировать код приложения, чтобы изменение стейта внутри компонента не повлекло за собой лишние ре-рендеры дочерних компонентов.
Компилятор будет оптимизировать только те компоненты, которые подходят под определенные условия. Одно из таких условий – идемпотентность компонента, т.е. всегда возвращает одинаковый результат при одном и том же наборе пропсов.
- Actions. Раньше предполагалось делать Actions, которые выполняют код на сервере – Server Actions. Сейчас API расширили и он стал называться просто Actions, поддерживая выполнение действия и на клиенте:
<form action={search}>
<input name="query" />
<button type="submit">Search</button>
</form>
Функцию в action можно будет определить как на сервере, используя директиву 'use server', так и на клиенте. При работе с action будут доступны хуки отслеживания состояния формы useFormStatus и useFormState.
- Директивы "use client" и "use server" будут определять «точки разделения» между окружениями. Директива "use client" будет сообщать бандлеру о необходимости генерации <noscript> (как в Astro Islands), а "use server" сообщит бандлеру о необходимости генерации POST endpoint (как в tRPC Mutations).
- Document Metadata. Появится встроенная поддержка для рендера <noscript>, <meta>, <link> в любом месте дерева компонентов, как в React Helmet.
- Загрузка ресурсов. Интеграция Suspense с жизненным циклом загрузки ресурсов, таких как стили, шрифты и скрипты.
https://react.dev/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024
react.dev
React Labs: What We've Been Working On – February 2024 – React
The library for web and native user interfaces
👍17❤1👎1
Анимация с учетом направления в Framer Motion
При добавлении анимации на сайт бывает нужно чтобы она двигалась в разных направлениях, в зависимости от контекста. Например, при навигации между экранами надо чтобы текущий экран уезжал влево, а новый экран выезжал справа. Когда возвращаешься назад, то анимация должна быть обратной.
Чтобы сделать такую анимацию можно использовать Framer Motion и создать переиспользуемый компонент, который можно использовать для разных ситуаций. Пример использования:
https://sinja.io/blog/direction-aware-animations-in-framer-motion
При добавлении анимации на сайт бывает нужно чтобы она двигалась в разных направлениях, в зависимости от контекста. Например, при навигации между экранами надо чтобы текущий экран уезжал влево, а новый экран выезжал справа. Когда возвращаешься назад, то анимация должна быть обратной.
Чтобы сделать такую анимацию можно использовать Framer Motion и создать переиспользуемый компонент, который можно использовать для разных ситуаций. Пример использования:
export const DirectionAwareAnimationsReusableSolution = () => {
const [product, setProduct] = useState<Product | null>(null);
const direction: Direction = product ? 'forward' : 'back';
return (<div>
<MotionConfig transition={{ type: 'spring', duration: 0.55 }}>
<AnimatePresenceWithDirection initial={false} mode="sync" direction={direction}>
{product
? <ProductDetails product={product} onBack={() => setProduct(null)} key="details" />
: <ProductsList openProduct={setProduct} key="list" />
}
</AnimatePresenceWithDirection>
</MotionConfig>
</div>);
};
export const ProductDetails = () => {
const animationProps = useDirectionAnimation();
return <motion.div {...animationProps}>
{/* ... */}
</motion.div>
});
https://sinja.io/blog/direction-aware-animations-in-framer-motion
sinja.io
Direction-aware animations in Framer Motion · OlegWock
Carousels, multistep forms and navigation between screens – they all profit from nice direction-aware animation. In this recipe, you'll learn how you can implement some.
👍5
Как использовать forwardRef с generic компонентами
Одно из ограничений forwardRef в том, что он отключает выведение типа для generic компонентов. Например:
Чтобы исправить выведение типа для generic компонентов, можно создать утилитарную функцию для вызова forwardRef с правильными типами:
https://www.totaltypenoscript.com/forwardref-with-generic-components
Одно из ограничений forwardRef в том, что он отключает выведение типа для generic компонентов. Например:
const Table = <T,>(
props: {
data: T[];
renderRow: (row: T) => React.ReactNode;
},
ref: React.ForwardedRef<HTMLTableElement>
) => {
/** --snip-- */
};
const ForwardReffedTable = React.forwardRef(Table);
<Table
data={["a", "b"]}
renderRow={(row) => { // Тип выводится: row: string
return <tr>{row}</tr>;
}}
/>;
<ForwardReffedTable
data={["a", "b"]}
renderRow={(row) => { // Тип не выводится: row: unknown
return <tr>{row}</tr>;
}}
/>;
Чтобы исправить выведение типа для generic компонентов, можно создать утилитарную функцию для вызова forwardRef с правильными типами:
function fixedForwardRef<T, P = {}>(
render: (props: P, ref: React.Ref<T>) => React.ReactNode
): (props: P & React.RefAttributes<T>) => React.ReactNode {
return React.forwardRef(render) as any;
}
// Вызов forwardRef с правильным выведением типа:
const ForwardReffedTable = fixedForwardRef(Table);
https://www.totaltypenoscript.com/forwardref-with-generic-components
Total TypeScript
How To Use forwardRef With Generic Components
Learn about the limitations of React's `forwardRef` TypeScript and discover a solution to enable inference on generic components.
👍8🔥3
React будет компилируемым
В блоге React рассказали, что React 19 будет компилируемым. В React Training объяснили, что это значит.
При использовании функциональных компонентов и хуков нужно самостоятельно мемоизировать компоненты, колбеки и переменные для предотвращения ненужных ререндеров. При использовании useCallback или useMemo нужно указывать массив зависимостей в deps, чтобы React пересоздавал функцию при изменении зависимостей.
Если использовать линтер для определения зависимостей exhaustive-deps, то он решает большинство проблем. Однако, зависимости, которые кладутся в массив, должны быть стабильными. Стабильная переменная – это переменная, которая не изменяется, если вы этого не захотите. Для стабилизации массивов, объектов и функций используется useMemo.
Для того чтобы избежать проблем с мемоизацией и определением массива зависимостей, в React 19 появится авто-мемоизация с помощью компилятора. Этот компилятор избавит разработчиков от ручной мемоизации, которая была основной проблемой при работе с функциональными компонентами и хуками.
https://reacttraining.com/blog/react-19-will-be-compiled
В блоге React рассказали, что React 19 будет компилируемым. В React Training объяснили, что это значит.
При использовании функциональных компонентов и хуков нужно самостоятельно мемоизировать компоненты, колбеки и переменные для предотвращения ненужных ререндеров. При использовании useCallback или useMemo нужно указывать массив зависимостей в deps, чтобы React пересоздавал функцию при изменении зависимостей.
Если использовать линтер для определения зависимостей exhaustive-deps, то он решает большинство проблем. Однако, зависимости, которые кладутся в массив, должны быть стабильными. Стабильная переменная – это переменная, которая не изменяется, если вы этого не захотите. Для стабилизации массивов, объектов и функций используется useMemo.
Для того чтобы избежать проблем с мемоизацией и определением массива зависимостей, в React 19 появится авто-мемоизация с помощью компилятора. Этот компилятор избавит разработчиков от ручной мемоизации, которая была основной проблемой при работе с функциональными компонентами и хуками.
https://reacttraining.com/blog/react-19-will-be-compiled
ReactTraining.com
React Will Be Compiled
React Corporate Workshops, Training, and Consulting
👍40👎3
Чему мы научились при переходе на Next.js 14 с серверными компонентами
Medusa.js – open source проект для создания e-commerce приложений. В своем блоге они рассказали, чему научились при переходе стартового шаблона на Next.js 14 с серверными компонентами.
Одна из новых особенностей, которая требует привыкания – механизм получения данных и кэширования. Кэширование работает автоматически для fetch запросов и каждый запрос кэшируется и хранится до ревалидации. Это избавляет от проп-дриллинга и передачи значений через контекст, данные можно сразу получить через fetch из кэша.
Серверные действия – асинхронные функции, которые вызываются на клиенте и выполняются на сервере. Под капотом происходит POST запрос на сервер. Используя серверные действия, не раскрываются API ключи и не надо беспокоиться о CORS.
Серверные компоненты позволяют создать UI, который полностью рендерится на сервере. Серверные компоненты имеют доступ к БД и API, что позволяет сократить количество запросов между клиентом и сервером. Например, по умолчанию в Medusa.js на странице карточки товара все компоненты серверные, за исключением кнопки добавления корзины.
С серверными компонентами возникает проблема при получении параметров URL. Т.к. в серверных компонентах не поддерживаются хуки, то единственный вариант получения параметров URL - в page.tsx, и уже дальше передать параметры через проп-дриллинг дочерним компонентам.
https://medusajs.com/blog/client-server-transition-learnings-nextjs-14-server-components/
Medusa.js – open source проект для создания e-commerce приложений. В своем блоге они рассказали, чему научились при переходе стартового шаблона на Next.js 14 с серверными компонентами.
Одна из новых особенностей, которая требует привыкания – механизм получения данных и кэширования. Кэширование работает автоматически для fetch запросов и каждый запрос кэшируется и хранится до ревалидации. Это избавляет от проп-дриллинга и передачи значений через контекст, данные можно сразу получить через fetch из кэша.
Серверные действия – асинхронные функции, которые вызываются на клиенте и выполняются на сервере. Под капотом происходит POST запрос на сервер. Используя серверные действия, не раскрываются API ключи и не надо беспокоиться о CORS.
Серверные компоненты позволяют создать UI, который полностью рендерится на сервере. Серверные компоненты имеют доступ к БД и API, что позволяет сократить количество запросов между клиентом и сервером. Например, по умолчанию в Medusa.js на странице карточки товара все компоненты серверные, за исключением кнопки добавления корзины.
С серверными компонентами возникает проблема при получении параметров URL. Т.к. в серверных компонентах не поддерживаются хуки, то единственный вариант получения параметров URL - в page.tsx, и уже дальше передать параметры через проп-дриллинг дочерним компонентам.
https://medusajs.com/blog/client-server-transition-learnings-nextjs-14-server-components/
Medusa
Medusa - What we've learned from the transition to Next.js 14 with Server Components
The most popular ecommerce project on GitHub. Medusa provides open-source ecommerce backend infrastructure in NodeJS.
👍10👎5🔥2
Избегаем ошибки гидратации с помощью useSyncExternalStore
При разработке SSR приложений может возникать ошибка:
Она связана с несоответствием контента между клиентом и сервером. Например, при рендере даты на сервере используется одна локаль 21/02/2024, а на клиенте другая локаль – пользовательская 2/21/2024.
Самый простой способ избавиться от ошибки - это использовать проп suppressHydrationWarning. Но как следует из документации React им нельзя злоупотреблять.
Другой способ – использовать useEffect для предотвращения рендера на сервере:
Т.к. на сервере useEffect не запускается, то это рабочий способ, но не самый оптимальный. При дальнейшей навигации по сайту компонент LastUpdated будет всегда рендериться дважды, хотя в этом не будет никакой нужды. Мы уже находимся на клиенте, но LastUpdated об этом не знает.
Еще один способ – использовать useSyncExternalStore. Хоть он и предназначен для подписки на внешний стор, у него есть вторая особенность: он позволяет различать clientSnapshot и serverSnapshot. При навигации по сайту он будет сразу брать clientSnapshot, а на сервере использовать serverSnapshot. Вместо подписки на внешний стор можно передать заглушку – пустую функцию. Пример использования:
https://tkdodo.eu/blog/avoiding-hydration-mismatches-with-use-sync-external-store
При разработке SSR приложений может возникать ошибка:
Uncaught Error: Text content does not match server-rendered HTML.Она связана с несоответствием контента между клиентом и сервером. Например, при рендере даты на сервере используется одна локаль 21/02/2024, а на клиенте другая локаль – пользовательская 2/21/2024.
Самый простой способ избавиться от ошибки - это использовать проп suppressHydrationWarning. Но как следует из документации React им нельзя злоупотреблять.
Другой способ – использовать useEffect для предотвращения рендера на сервере:
function LastUpdated() {
const [isClient, setIsClient] = React.useState(false)
React.useEffect(() => {
setIsClient(true)
}, [])
if (!isClient) {
return null
}
/** --snip-- */
}
Т.к. на сервере useEffect не запускается, то это рабочий способ, но не самый оптимальный. При дальнейшей навигации по сайту компонент LastUpdated будет всегда рендериться дважды, хотя в этом не будет никакой нужды. Мы уже находимся на клиенте, но LastUpdated об этом не знает.
Еще один способ – использовать useSyncExternalStore. Хоть он и предназначен для подписки на внешний стор, у него есть вторая особенность: он позволяет различать clientSnapshot и serverSnapshot. При навигации по сайту он будет сразу брать clientSnapshot, а на сервере использовать serverSnapshot. Вместо подписки на внешний стор можно передать заглушку – пустую функцию. Пример использования:
const emptySubscribe = () => () => {}
function LastUpdated() {
const date = React.useSyncExternalStore(
emptySubscribe,
() => lastUpdated.toLocaleDateString(),
() => lastUpdated.toLocaleDateString('en-US')
)
return <span>Last updated at: {date}</span>
}
https://tkdodo.eu/blog/avoiding-hydration-mismatches-with-use-sync-external-store
tkdodo.eu
Avoiding Hydration Mismatches with useSyncExternalStore
Avoiding hydration mismatches can usually be done in two ways - suppressing the warning or spawning an effect. But is there a third option ... ?
👍17
Functional UI Kit – унифицированный UI кит для Figma и React
Functional UI Kit – это UI кит для React, который содержит такие же компоненты для Figma. Для каждого компонента и его варианта есть своя история в Storybook. Между компонентами в Figma и React сохранены названия пропсов, а также сохранены одинаковые названия переменных в Figma и в CSS переменных.
При разработке библиотеки автор старался унифицировать все компоненты и использовать общие термины. Благодаря единообразию, продуктовая команда может общаться на одном языке и более быстро разрабатывать продукт.
https://functional-ui-kit.com/
Functional UI Kit – это UI кит для React, который содержит такие же компоненты для Figma. Для каждого компонента и его варианта есть своя история в Storybook. Между компонентами в Figma и React сохранены названия пропсов, а также сохранены одинаковые названия переменных в Figma и в CSS переменных.
При разработке библиотеки автор старался унифицировать все компоненты и использовать общие термины. Благодаря единообразию, продуктовая команда может общаться на одном языке и более быстро разрабатывать продукт.
https://functional-ui-kit.com/
Functional-Ui-Kit
Functional UI Kit
Precise Figma & React components. Packed with best practices, unified prop names & consistent design and code. An essential toolkit that's built to simplify communication between designers and developers.
👍18
Изучаем четыре ключевых примитива из Solid
Сравнение ключевых примитивов Solid и мета фреймворка Solid Start с Next.js/Remix:
- Сигналы. Детальная реактивность с помощью сигналов – суперсила Solid. Например, createAsync возвращает сигнал, который триггерит Suspense в зависимости от того, где происходит доступ к значению:
В React, чтобы сработал Suspense, нужно чтобы вложенный компонент был асинхронным.
- Неблокирующий Async. В Solid createAsync – неблокирующая рендер функция, т.к. для получения результата не требуется использовать async/await.
- Серверные функции. Преимущество серверных функций в возможности типизировать запросы между сервером и клиентом. Solid Start по умолчанию изоморфен, если не указана директива 'use server'. В Solid серверные функции можно вызывать внутри других серверных функций. Также во время SSR серверные функции можно вызывать внутри компонентов:
- Серверные действия. Они предназначены для изменения данных на сервере. В Solid они отличаются от Remix. Пример:
Хук useSubmission используется для вызова действия в любом компоненте приложения. Еще одно отличие от Remix в том, что можно создать несколько серверных действий и вызывать их в любых компонентах, а не только в роутах.
https://www.brenelz.com/posts/learning-four-key-primitives-in-solid/
Сравнение ключевых примитивов Solid и мета фреймворка Solid Start с Next.js/Remix:
- Сигналы. Детальная реактивность с помощью сигналов – суперсила Solid. Например, createAsync возвращает сигнал, который триггерит Suspense в зависимости от того, где происходит доступ к значению:
// Solid
export default function Home() {
const nums = createAsync(() => getNums());
return (
<Suspense fallback="Loading...">
{nums()}
</Suspense>
);
}
В React, чтобы сработал Suspense, нужно чтобы вложенный компонент был асинхронным.
- Неблокирующий Async. В Solid createAsync – неблокирующая рендер функция, т.к. для получения результата не требуется использовать async/await.
- Серверные функции. Преимущество серверных функций в возможности типизировать запросы между сервером и клиентом. Solid Start по умолчанию изоморфен, если не указана директива 'use server'. В Solid серверные функции можно вызывать внутри других серверных функций. Также во время SSR серверные функции можно вызывать внутри компонентов:
// Solid
export const getUser = async id => {
"use server";
return {
user: await db.findUser(id),
friends: getFriends(id),
};
};
export function UserComponent(props) {
const user = createAsync(() => getUser(props.userId));
/** --snip-- */
}
- Серверные действия. Они предназначены для изменения данных на сервере. В Solid они отличаются от Remix. Пример:
// Solid
const addNum = action(async () => {
"use server";
return 'success';
}, 'addNum');
export default function Home() {
const addNumAction = useSubmission(addNum);
return (
<main>
<form action={addNum} method='post'>
<button type="submit">Add Num</button>
</form>
<pre>
{addNumAction.pending ? 'pending' : 'not pending'}
{addNumAction.result}
</pre>
</main>
);
}
Хук useSubmission используется для вызова действия в любом компоненте приложения. Еще одно отличие от Remix в том, что можно создать несколько серверных действий и вызывать их в любых компонентах, а не только в роутах.
https://www.brenelz.com/posts/learning-four-key-primitives-in-solid/
Brenelz
Learning Four Key Primitives In Solid
A website containing blog posts related to the frontend.
👍13❤1
Без внешнего margin
При создании переиспользуемых компонентов есть анти-паттерны, которые усложняют использование компонентов. Один из таких анти-паттернов – внешний margin (и иногда padding).
Внешний margin - это margin, который выходит за рамки border-box компонента. Пример:
Внешний margin плох тем, что наделяет компонент ответственностью за внешнее пространство. Компонент должен отвечать только за внутреннее пространство.
Для создания пространства между компонентами используйте Flex или Grid вместе с gap в родительском компоненте.
https://kyleshevlin.com/no-outer-margin/
При создании переиспользуемых компонентов есть анти-паттерны, которые усложняют использование компонентов. Один из таких анти-паттернов – внешний margin (и иногда padding).
Внешний margin - это margin, который выходит за рамки border-box компонента. Пример:
function Card({ children }) {
return (
// Так нельзя, это внешний margin
<div style={{ marginBottom: '1rem' }}>{children}</div>
)
}
function EmployeeCard({ name, occupation }) {
return (
<Card>
// Так можно, это внутренний margin
<div style={{ marginBottom: '1rem' }}>{name}</div>
<div>{occupation}</div>
</Card>
)
}
Внешний margin плох тем, что наделяет компонент ответственностью за внешнее пространство. Компонент должен отвечать только за внутреннее пространство.
Для создания пространства между компонентами используйте Flex или Grid вместе с gap в родительском компоненте.
https://kyleshevlin.com/no-outer-margin/
Kyle Shevlin's Blog
No Outer margin | Kyle Shevlin
It is an anti-pattern to add margin to the outermost element of a component. It breaks encapsulation and makes components difficult to reuse. Let's learn what to do instead.
👍25
"react-strict-dom" – почему это круто?
Совсем недавно вышла библиотека "react-strict-dom", цель которой улучшить и стандартизировать способ написания компонентов для Web и Native приложений.
Чтобы понять, как работает эта библиотека, сначала стоит разобраться как работает существующий подход к написанию универсальных компонентов через react-native-web. В библиотеке react-native-web используются React Native компоненты. Библиотека преобразует примитивы React Native в примитивы React Dom, позволяя отображать React Native компоненты в Web.
react-strict-dom использует другой подход, в ней используется Web API для рендера компонентов. Библиотека использует полифилы для перевода примитивов в react-native и react-dom для отображения в Native и Web соответственно.
Под капотом используется stylex, пример компонента с react-strict-dom:
https://szymonrybczak.dev/blog/react-strict-dom
Совсем недавно вышла библиотека "react-strict-dom", цель которой улучшить и стандартизировать способ написания компонентов для Web и Native приложений.
Чтобы понять, как работает эта библиотека, сначала стоит разобраться как работает существующий подход к написанию универсальных компонентов через react-native-web. В библиотеке react-native-web используются React Native компоненты. Библиотека преобразует примитивы React Native в примитивы React Dom, позволяя отображать React Native компоненты в Web.
react-strict-dom использует другой подход, в ней используется Web API для рендера компонентов. Библиотека использует полифилы для перевода примитивов в react-native и react-dom для отображения в Native и Web соответственно.
Под капотом используется stylex, пример компонента с react-strict-dom:
import React from "react";
import { css, html } from "react-strict-dom";
export default function App() {
return (
<html.div style={styles.div}>
<html.div data-testid="testid">div</html.div>
<html.p>paragraph</html.p>
<html.div />
<html.span>
<html.a href="https://google.com">anchor</html.a>,
</html.div>
);
}
const styles = css.create({
div: {
paddingBottom: 50,
backgroundColor: "white",
},
});
https://szymonrybczak.dev/blog/react-strict-dom
szymonrybczak.dev
"react-strict-dom", why it's so great?
react-strict-dom, why it's so great?
👎14👍5
HTML стриминг и алгоритм сравнения DOM
В последние годы все больше браузеров поддерживают стриминг HTML и JS. Во время стриминга браузер сразу рендерит HTML и исполняет JS после получения HTML фрагмента с сервера. Для активации стриминга нужно отправить браузеру правильные заголовки:
В сервера в ответе нужно использовать ReadableStream.
Есть несколько практических вариантов использования стриминга HTML. Один из них – замена HTML во время стриминга, например, как в React Suspense. Самая простая реализация React Suspense заключается в следующем: передать заглушку контента и JS скрипт для замены данных. При загрузке нужных данных передать шаблон нужного контента и вызвать функцию замены данных. Пример:
Если во время стриминга HTML браузер получит JS, то он его выполнит.
Также с помощью HTML стриминга и View Transitions API можно реализовать SPA приложение, заменяя HTML без перезагрузки страницы. Если использовать алгоритмы сравнения DOM, то вместо полной замены HTML на странице можно будет менять его частично, т.е. лишь те места, которые поменялись.
https://aralroca.com/blog/html-node-streaming
В последние годы все больше браузеров поддерживают стриминг HTML и JS. Во время стриминга браузер сразу рендерит HTML и исполняет JS после получения HTML фрагмента с сервера. Для активации стриминга нужно отправить браузеру правильные заголовки:
{
"transfer-encoding": "chunked",
"vary": "Accept-Encoding",
"content-type": "text/html; charset=utf-8"
}
В сервера в ответе нужно использовать ReadableStream.
Есть несколько практических вариантов использования стриминга HTML. Один из них – замена HTML во время стриминга, например, как в React Suspense. Самая простая реализация React Suspense заключается в следующем: передать заглушку контента и JS скрипт для замены данных. При загрузке нужных данных передать шаблон нужного контента и вызвать функцию замены данных. Пример:
return new Response(
new ReadableStream({
async start(controller) {
const suspensePromises = []
controller.enqueue(encoder.encode('<html lang="en">'))
/** --snip-- */
// Передача заглушки
controller.enqueue(
encoder.encode('<div id="suspensed:1">Loading...</div>')
)
suspensePromises.push(
computeExpensiveChunk().then((content) => {
// Шаблон нужного контента
controller.enqueue(
encoder.encode(
`<template id="suspensed-content:1">${content}</template>`
)
)
// Передать скрипт для замены заглушки на нужный контент
controller.enqueue(encoder.encode(`<noscript>unsuspense('1')</noscript>`))
})
)
/** --snip-- */
},
})
)
Если во время стриминга HTML браузер получит JS, то он его выполнит.
Также с помощью HTML стриминга и View Transitions API можно реализовать SPA приложение, заменяя HTML без перезагрузки страницы. Если использовать алгоритмы сравнения DOM, то вместо полной замены HTML на странице можно будет менять его частично, т.е. лишь те места, которые поменялись.
https://aralroca.com/blog/html-node-streaming
Aralroca
HTML Streaming and DOM Diffing Algorithm
Explore HTML Streaming and its benefits covering topics from initial loading to advanced cases like navigation, RPCs, and DOM diffing algorithms during streaming
👍9❤2
Лучшие практики по написанию тестов с React Testing Library
Правильно написанные тесты не только предотвращают ошибки в коде, но и в случае React Testing Library улучшают доступность компонента. В своем блоге Алекс Хоменко собрал лучшие практики по написанию тестов с React Testing Library, кратко о них:
- Использовать *ByRole селекторы. Они наиболее надежны и работают для многих элементов. Использование *ByRole селекторов приучают к написанию доступных компонентов. Пример:
- Использовать userEvent вместо fireEvent для симуляции событий. Хотя fireEvent работает во многих случаях, это легкая обертка над dispatchEvent. Также, в новых версиях React Testing Library рекомендуется инициализировать userEvent перед использованием:
- Использовать findBy* вместо waitFor. Бывают случаи, когда элемент, который пытаемся использовать, недоступен при первоначальном рендеринге и появится после получения данных из API. Для таких сценариев можно использовать асинхронный селектор findBy*, который внутри себя использует waitFor. Пример:
- Исправление ошибки
Еще одна из причин данной ошибки может быть неправильная работа с таймерами. Если компонент использует таймеры, то используйте моки для таймеров (useFakeTimers).
https://claritydev.net/blog/improving-react-testing-library-tests
Правильно написанные тесты не только предотвращают ошибки в коде, но и в случае React Testing Library улучшают доступность компонента. В своем блоге Алекс Хоменко собрал лучшие практики по написанию тестов с React Testing Library, кратко о них:
- Использовать *ByRole селекторы. Они наиболее надежны и работают для многих элементов. Использование *ByRole селекторов приучают к написанию доступных компонентов. Пример:
it("should submit correct form data", async () => {
/** --snip-- */
await user.type(screen.getByRole("textbox", { name: "Name" }), "Test");
await user.click(screen.getByRole("button", { name: "Sign up" }));
/** --snip-- */
});
- Использовать userEvent вместо fireEvent для симуляции событий. Хотя fireEvent работает во многих случаях, это легкая обертка над dispatchEvent. Также, в новых версиях React Testing Library рекомендуется инициализировать userEvent перед использованием:
it("should save correct data on submit", async () => {
/** --snip-- */
const user = userEvent.setup();
await user.type(screen.getByRole("textbox", { name: "Name" }), "Test");
/** --snip-- */
});
- Использовать findBy* вместо waitFor. Бывают случаи, когда элемент, который пытаемся использовать, недоступен при первоначальном рендеринге и появится после получения данных из API. Для таких сценариев можно использовать асинхронный селектор findBy*, который внутри себя использует waitFor. Пример:
it("renders without breaking", async () => {
render(<ListPage />);
expect(
await screen.findByRole("heading", { name: "List of items" }),
).toBeInTheDocument();
});
- Исправление ошибки
Fixing the "not wrapped in act(...)" warnings. Одной из причин ошибки может быть использование getBy* вместо findBy*, поскольку рассматриваемый элемент или компонент обновляется после асинхронного действия. Еще одна из причин данной ошибки может быть неправильная работа с таймерами. Если компонент использует таймеры, то используйте моки для таймеров (useFakeTimers).
https://claritydev.net/blog/improving-react-testing-library-tests
claritydev.net
Best Practices for Writing Tests with React Testing Library
Properly written tests not only help prevent regressions and buggy code, but in the case of React Testing Library, they also improve the accessibility of components and the overall user experience. In this post, we'll explore how to get the most out of your…
👍16🔥1
This media is not supported in your browser
VIEW IN TELEGRAM
Million Lint вышел в публичную бету
Million Lint – это VSCode расширение, которое выявляет медленный код и предлагает способы его оптимизации. Это как ESLint, но для производительности.
С помощью линтера можно узнать сколько времени в среднем занимает рендер компонента, как в React Devtools, но прямо в коде редактора. Также линтер дает возможность отследить размер бандла, сетевые запросы, пропсы, вызовы хуков.
Для того чтобы использовать линтер, надо установить VSCode расширение и добавить MillionCompiler, который выполняет динамический анализ отдельных компонентов. Пример настройки для Vite приложения:
https://million.dev/blog/lint
Million Lint – это VSCode расширение, которое выявляет медленный код и предлагает способы его оптимизации. Это как ESLint, но для производительности.
С помощью линтера можно узнать сколько времени в среднем занимает рендер компонента, как в React Devtools, но прямо в коде редактора. Также линтер дает возможность отследить размер бандла, сетевые запросы, пропсы, вызовы хуков.
Для того чтобы использовать линтер, надо установить VSCode расширение и добавить MillionCompiler, который выполняет динамический анализ отдельных компонентов. Пример настройки для Vite приложения:
import MillionCompiler from "@million/lint";
import react from "@vitejs/plugin-react";
import { defineConfig } from "vite";
export default defineConfig({
plugins: [MillionCompiler.vite(), react()],
});
https://million.dev/blog/lint
👍24🔥7
В React 19 добавят diff предупреждения гидратации
В React 19 планируют писать в консоле diff предупреждения гидратации. На данный момент, в React 18, если во время гидратации клиентская и серверная разметка отличаются, то React просто предупреждает о том, что есть несоответствие UI между клиентом и сервером.
В React 19 при определении diff будет учитываться работа хука useSyncExternalStore, который позволяет возвращать разный результат для клиента и сервера.
Вместо нескольких предупреждений в консоле будет одно общее, пример:
https://github.com/facebook/react/pull/28512
В React 19 планируют писать в консоле diff предупреждения гидратации. На данный момент, в React 18, если во время гидратации клиентская и серверная разметка отличаются, то React просто предупреждает о том, что есть несоответствие UI между клиентом и сервером.
В React 19 при определении diff будет учитываться работа хука useSyncExternalStore, который позволяет возвращать разный результат для клиента и сервера.
Вместо нескольких предупреждений в консоле будет одно общее, пример:
Warning: A tree hydrated but some attributes of the server rendered HTML didn't match the client properties. This won't be patched up. This can happen if a SSR-ed Client Component used:
--snip--
<Mismatch isClient={true}>
<div className="parent">
<main
+ className="child client"
- className="child server"
+ dir="ltr"
- dir="rtl"
>
https://github.com/facebook/react/pull/28512
GitHub
Add Diffs to Hydration Warnings by sebmarkbage · Pull Request #28512 · facebook/react
Stacked on #28502.
This builds on the mechanism in #28502 by adding a diff of everything we've collected so far to the thrown error or logged error.
This isn't actually a longest common sub...
This builds on the mechanism in #28502 by adding a diff of everything we've collected so far to the thrown error or logged error.
This isn't actually a longest common sub...
👍18❤5
Встроенная база данных Astro DB
В Astro появилась встроенная локальная база данных Astro DB. В ней можно хранить статьи, комментарии и пользователей, создавая разные таблицы.
БД сделана на основе libSQL. Изначально планировалось использовать SQLite, но из-за того, что SQLite нельзя использовать в браузере, выбрали libSQL. Его можно запустить в StackBlitz через WASM.
Astro DB устроена таким образом, что при каждом запуске дев-сервера БД (data.db) создается заново. При запуске команды astro dev происходит следующее:
- Создается пустая БД
- Создается схема БД из
- Заполняется БД из
Это дает вам простую, воспроизводимую БД, которая в дальнейшем служит единственным источником данных для приложения. Пример файла конфигурации БД
При работе с БД доступны основные операции записи, чтения, фильтрации, сравнения и объединения. Пример запроса:
https://astro.build/blog/astro-db-deep-dive/
В Astro появилась встроенная локальная база данных Astro DB. В ней можно хранить статьи, комментарии и пользователей, создавая разные таблицы.
БД сделана на основе libSQL. Изначально планировалось использовать SQLite, но из-за того, что SQLite нельзя использовать в браузере, выбрали libSQL. Его можно запустить в StackBlitz через WASM.
Astro DB устроена таким образом, что при каждом запуске дев-сервера БД (data.db) создается заново. При запуске команды astro dev происходит следующее:
- Создается пустая БД
.astro/data.db- Создается схема БД из
db/config.ts- Заполняется БД из
db/seed.tsЭто дает вам простую, воспроизводимую БД, которая в дальнейшем служит единственным источником данных для приложения. Пример файла конфигурации БД
db/config.ts:
import { defineDb, defineTable, column } from 'astro:db';
const Comment = defineTable({
columns: {
author: column.text(),
body: column.text(),
}
})
export default defineDb({
tables: { Comment },
})
При работе с БД доступны основные операции записи, чтения, фильтрации, сравнения и объединения. Пример запроса:
import { db, eq, Comment, Author } from 'astro:db';
const comments = await db.select()
.from(Comment)
.innerJoin(Author, eq(Comment.authorId, Author.id));
https://astro.build/blog/astro-db-deep-dive/
Astro
Astro DB: A Deep Dive | Astro
Yesterday we launched a fully managed SQL database service designed exclusively for the Astro web framework. Let's dive into the implementation details of Astro DB: how it works, why we built it, and why we're adopting libSQL.
👍13👎1🔥1
React Data Grid: быстрый датагрид как Excel
react-data-grid – компонент для отображения данных в датагриде. Компонент поддерживает виртуализацию, tree-shaking, используется только одна внешняя зависимость, а gzip размер пакета 14.5 КБ.
Компонент поддерживает фильтрацию, группировку, выбор строк, форматирование ячеек, бесконечную подгрузку и многое другое при работе с данными. На сайте есть много готовых примеров, среди которых пример рендера миллиона ячеек.
https://adazzle.github.io/react-data-grid/
react-data-grid – компонент для отображения данных в датагриде. Компонент поддерживает виртуализацию, tree-shaking, используется только одна внешняя зависимость, а gzip размер пакета 14.5 КБ.
Компонент поддерживает фильтрацию, группировку, выбор строк, форматирование ячеек, бесконечную подгрузку и многое другое при работе с данными. На сайте есть много готовых примеров, среди которых пример рендера миллиона ячеек.
import 'react-data-grid/lib/styles.css';
import DataGrid from 'react-data-grid';
const columns = [
{ key: 'id', name: 'ID' },
{ key: 'noscript', name: 'Title' }
];
const rows = [
{ id: 0, noscript: 'Example' },
{ id: 1, noscript: 'Demo' }
];
function App() {
return <DataGrid columns={columns} rows={rows} />;
}
https://adazzle.github.io/react-data-grid/
👍28
В React нельзя сделать типобезопасным children
При работе с TypeScript и JSX иногда появляются ситуации, когда надо ограничить дочерние элементы определенным типом. Например, есть компонент Select, который принимает компоненты Option как дочерние. Вы хотите убедиться, что компоненты Option единственные дочерние компоненты, которые передаются в Select:
Ограничить тип компонента, передаваемого как дочерний, через TypeScript невозможно. Любой <Component /> или HTML тег это JSX.Element.
В качестве заключения стоит отметить, что главная магия React в композиции. Можно компоновать компоненты любым удобным способом. Ограничивая children у компонента, вы нарушаете эту возможность компоновки.
Возможно вместо потенциального ограничения типа children можно использовать другой проп:
https://www.totaltypenoscript.com/type-safe-children-in-react-and-typenoscript
При работе с TypeScript и JSX иногда появляются ситуации, когда надо ограничить дочерние элементы определенным типом. Например, есть компонент Select, который принимает компоненты Option как дочерние. Вы хотите убедиться, что компоненты Option единственные дочерние компоненты, которые передаются в Select:
// Хорошо
<Select>
<Option value="1">One</Option>
<Option value="2">Two</Option>
<Option value="3">Three</Option>
</Select>
// Плохо
<Select>
<div>One</div>
<div>Two</div>
<div>Three</div>
</Select>
Ограничить тип компонента, передаваемого как дочерний, через TypeScript невозможно. Любой <Component /> или HTML тег это JSX.Element.
В качестве заключения стоит отметить, что главная магия React в композиции. Можно компоновать компоненты любым удобным способом. Ограничивая children у компонента, вы нарушаете эту возможность компоновки.
Возможно вместо потенциального ограничения типа children можно использовать другой проп:
<Select
options={[
{ value: "1", label: "One" },
{ value: "2", label: "Two" },
{ value: "3", label: "Three" },
]}
/>
https://www.totaltypenoscript.com/type-safe-children-in-react-and-typenoscript
Total TypeScript
You Can't Make Children "Type Safe" in React & TypeScript
Learn why it's not possible to restrict the type of children in React components with TypeScript.
👍22👎1