Обложка канала

Заметки про React. Страница 4

Короткие и полезные заметки про React

  • Заметки про React

    Гайд по сужению типов в TypeScript В своем блоге Владимир Клепов написал подробный гайд по сужению типов в TypeScript. Сужение типов полезно, когда получаешь данные извне: читаешь localStorage, парсишь URL или читаешь пользовательский ввод. Также сужение типа применяется, когда мы расширяем API для удобства, например, вызов функции mount('#mount’) и mount(document.querySelector('#mount’)). В статье рассматриваются несколько методов безопасного сужения типов: - Использование нативных JS операторов, таких как typeof, === и других. - Использование type guard функций. Они полезны, но, как ни странно, небезопасны для типов. - Написать функцию приведения типа, например (x: unknown) => number. blog.thoughtspile.tech/2023/01…e-narrow
  • Заметки про React

    Что мне нравится в Astro Дэйв Рэмси сравнивает Astro с Next.js и Gatsby. Автор заметил, что Next.js не очень подходит для сайта-блога. При посещении сайта пользователю приходится загружать слишком много JS, чтобы просто прочитать статью. При переводе сайта с Next.js на Astro автор заметил некоторые вещи, которые нравятся больше, чем в Next.js и Gatsby. - Поддерживает любой источник данных Astro никак не ограничивает способ получения данных. Он поддерживает ключевое слово await в шаблоне, поэтому можно записать fetch в переменную и использовать ее в шаблоне:
    ---
    const response = await fetch('https://an-endpoint.com/api');
    const data = await response.json();
    ---
    <p>{data.a_property}</p>
    
    - Есть готовые интеграции В Astro есть гайды по генерации sitemap, RSS ленты и оптимизации изображений. Также есть готовые модули для работы с СЕО. - Сложно написать непроизводительное приложение По умолчанию в Astro не создается JS для клиента. Создавать Astro компоненты легко, особенно после опыта написания React компонентов. - Большая часть API построена на веб-стандартах Большая часть низкоуровневого API Astro основана на веб-стандартах, поэтому его проще изучить. Например, Astro.request является объектом Request из Fetch API. www.macarthur.me/posts/w…ut-astro
    What I Like About Astro

    This site’s been on Next.js since 2021, and I’ve had very few complaints about it. I’ve appreciated things like its slick developer experience, the first-class support it enjoys from Vercel for features like incremental static regeneration, and the constant innovation driven by its community. It’s a great tool, and my...

    Alex MacArthur
  • Заметки про React

    Переезд с Webpack на Vite для микрофронтенда История переезда проекта со сборщика Webpack на Vite. Особенность проекта в том, что это микрофронтенд и используется SingleSPA и SystemJS importmap. В свою очередь Vite использует нативные ES6 модули в dev режиме для быстрой разработки. Поэтому использование SystemJS будет только замедлять сборку. Чтобы решить проблему, нужно перейти на нативный importmap и отказаться от SystemJS. https://habr.com/ru/company/dododev/blog/715988/
    Вдали от Webpack, или Как мы в Dodo микрофронтенды на Vite переводили

    В первой статье мы рассказывали, как вообще в Dodo появились микрофронтенды. Это продолжение. В общем, мы спокойно сидели и пилили новый проект на нашем устоявшемся стеке React + TypeScript + Webpack...

    Хабр
  • Реклама

  • Заметки про React

    Гайд по обработке ошибок в React В блоге Нади Макаревич вышел гайд по обработке ошибок в React. В первую очередь стоит понять, почему необходимо обрабатывать ошибки. При возникновении ошибки в одной из фаз жизненного цикла React произойдет размонтирование всего приложения, и пользователь увидит белый экран. Для того, чтобы предотвратить такие ситуации и отловить ошибку во время фазы жизненного цикла React, есть Error Boundaries. У него есть минусы: если возникла ошибка в колбеке или в асинхронном коде, то такая ошибка не дойдет до Error Boundaries:
    const Component = () => {
      useEffect(() => {
        // Будет обработан ErrorBoundary
        throw new Error('Destroy everything!');
      }, [])
    
      const onClick = () => {
        // Не будет обработан ErrorBoundary
        throw new Error('Hulk smash!');
      }
    
      useEffect(() => {
        // Если возникнет ошибка, то не будет обработан ErrorBoundary
        fetch('/bla')
      }, [])
    
      return <button onClick={onClick}>click me</button>
    }
    
    const ComponentWithBoundary = () => {
      return (
        <ErrorBoundary>
          <Component />
        </ErrorBoundary>
      )
    }
    
    Чтобы направить ошибку из колбека или асинхронного кода в ErrorBoundary, можно перекинуть ошибку в React используя трюк:
    const onClick = () => {
      try {
        //–snip–
      } catch (e) {
        // вызвать обновление стейта с выбросом ошибки
        setState(() => throw e);
      }
    }
    
    Чтобы самостоятельно не изобретать колесо, рекомендуется использовать библиотеку react-error-boundary. В ней уже решены основные проблемы, возникающие при обработке ошибок. www.developerway.com/posts/h…in-react
    GitHub - bvaughn/react-error-boundary: Simple reusable React error boundary component

    Simple reusable React error boundary component. Contribute to bvaughn/react-error-boundary development by creating an account on GitHub.

    GitHub
  • Заметки про React

    React.js: Документальный фильм Вышел документальный фильм про React. В нем рассказывается история создания библиотеки от самих разработчиков. В фильме снялись Том Оккино, Кристофер Чедо, Пит Хант, Себастьян Маркбоге, Дэн Абрамов и многие другие. https://www.youtube.com/watch?v=8pDqJVdNa44
    React.js: The Documentary

    React is easily one of the single most popular libraries in use today. Given that it was made within a juggernaut like Facebook, you might have assumed it was always destined for success. What if we told you that React’s first brush with the public sphere was anything but glamorous? React.js: The Documentary brings you the full story behind the early days of React, focusing on the dedicated group of developers who helped bring it to the world stage. This story is told by an all-star cast of developers like Tom Occhino, Christopher Chedeau, Pete Hunt, Sebastian Markbåge, Dan Abramov, and many more. Check out the home for untold developer stories around open source, careers and all the other cool stuff developers are doing at cult.honeypot.io. Honeypot is a developer-focused job platform, on a mission to get developers great jobs. Wanna see what we're all about? Visit honeypot.io to find a job you love. To learn more about Honeypot: http://www.honeypot.io/?utm_source=youtube Follow the amazing cast: Adam Wolff: https://twitter.com/dmwlff Andrew Clark: https://twitter.com/acdlite Christopher Chedeau: https://twitter.com/Vjeux Dan Abramov: https://twitter.com/dan_abramov David Nolen: https://twitter.com/swannodette Lee Byron: https://twitter.com/leeb Michael Chan: https://twitter.com/chantastic Pete Hunt: https://twitter.com/floydophone Sebastian Markbåge: https://twitter.com/sebmarkbage Shane O'Sullivan: https://twitter.com/Chofter and https://chofter.com/ Sophie Alpert: https://twitter.com/sophiebits and https://sophiebits.com/ Tom Occhino: https://twitter.com/tomocchino Tony Casparro: https://twitter.com/IamTonyC Many people contributed to React throughout the years and this documentary is just a little slice of that history. You can find full acknowledgements here: https://reactjs.org/acknowledgements.html Follow us: Twitter: https://twitter.com/honeypotio Facebook: https://www.facebook.com/Honeypotio/ LinkedIn: https://www.linkedin.com/company/honeypotio/ Instagram: https://www.instagram.com/honeypot.cult/

    YouTube
  • Заметки про React

    React рекурсивно ре-рендерит дочерние компоненты, но есть нюанс Алекс Сидоренко в своем блоге сделал наглядную визуализацию того, как разделение компонентов и использование совместного размещения состояния (state colocation) позволяют предотвращать лишний рендер. alexsidorenko.com/blog/re…ren-prop
    React recursively re-renders child components, but there is a nuance

    Component composition as an alternative to memoization

    Alexsidorenko
  • Заметки про React

    PR с заменой CRA на Vite В репозитории reactjs.org стал популярным PR с предложением использовать Vite вместо CRA. На это предложение ответил Дэн Абрамов. В своем комментарии он рассказал историю CRA и ее цели. Основной посыл заключается в том, что команда React не хочет забрасывать CRA, но и не хочет его рекомендовать. Важно создавать новые приложения с поддержкой SSG/SSR. В CRA же нет поддержки SSG/SSR. Одной из целей развития CRA видят превращение его в CLI лаунчер: давать на выбор фреймворк для создания приложения: Next.js, Remix, CRA и т.д. Также возможно в будущем react-scripts для сборки приложения перейдет на Vite. github.com/reactjs…ull/5487
    Replace Create React App recommendation with Vite by t3dotgg · Pull Request #5487 · reactjs/reactjs.org

    Create React App is not a great recommendation to be making, especially for newer developers. As an educator, I run into countless issues w/ new React devs running into unnecessary issues due to th...

    GitHub
  • Заметки про React

    Релиз TypeScript 5.0 Beta На днях вышел релиз мажорной версии TypeScript 5.0 Beta. В этом релизе много новых фич. Кратко о некоторых фичах релиза: Новый стандарт декораторов В TS уже были декораторы, и они были доступны под флагом. В версии 5.0 декораторы будут доступны без флага, а те декораторы, которые были доступны под флагом, объявлены как легаси. Пример использования новых декораторов:
    function loggedMethod<This, Args extends any[], Return>(
        target: (this: This, ...args: Args) => Return,
        context: ClassMethodDecoratorContext<This, (this: This, ...args: Args) => Return>
    ) {
        const methodName = String(context.name);
    
        function replacementMethod(this: This, ...args: Args): Return {
            console.log(`LOG: Entering method '${methodName}'.`)
            const result = target.call(this, ...args);
            console.log(`LOG: Exiting method '${methodName}'.`)
            return result;
        }
    
        return replacementMethod;
    }
    
    class Person {
        // --snip--
    
        @bound
        @loggedMethod
        greet() {
            console.log(`Hello, my name is ${this.name}.`);
        }
    }
    
    Для работы с декораторами добавлен новый тип ClassMethodDecoratorContext, который моделирует объект контекста, который принимают декораторы метода. У метода может быть несколько декораторов. В примере сначала выполнится @loggedMethod, а потом @bound. Декораторы можно использовать не только у метода, но и у класса и его полей. const как параметр типа В TS есть утверждение as const, которое делает выражение иммутабельным и говорит компилятору, что тип больше не будет расширяться. В TS 5.0 можно добавить const как модификатор к объявлению типа параметра. Пример:
    type HasNames = { names: readonly string[] };
    function getNamesExactly<const T extends HasNames>(arg: T): T["names"] {
        return arg.names;
    }
    
    // readonly ["Alice", "Bob", "Eve"]
    const names = getNamesExactly({ names: ["Alice", "Bob", "Eve"] });
    
    При этом использование модификатора const не требует ограничения типа в виде readonly. Если не использовать readonly, то результат будет другим:
    declare function fnBad<const T extends string[]>(args: T): void;
    
    // 'T' будет 'string[]', а не 'readonly ["a", "b", "c"]'
    fnBad(["a", "b" ,"c"]);
    
    Несколько конфиг файлов в extends Появилась возможность расширять tsconfig от несколько файлов:
    {
        "compilerOptions": {
            "extends": ["a.json", "b.json", "c.json"]
        }
    }
    
    В этом случае b.json расширяет a.json, а c.json расширяет b.json, а текущий конфиг расширяет c.json. devblogs.microsoft.com/typescr…5-0-beta
    Announcing TypeScript 5.0 Beta

    Today we’re excited to announce our beta release of TypeScript 5.0! This release brings many new features, while aiming to make TypeScript, smaller, simpler, and faster. We’ve implemented the new decorators standard, functionality to better support ESM projects in Node and bundlers,

    TypeScript
  • Заметки про React

    Новая документация React готова на 99% По сообщению Дэна Абрамова для того, чтобы вывести новую документацию из беты, осталось сделать сайт лендинга. Новая документация по React уже готова и она отличается от текущей: - Все примеры приведены с использованием хуков, а не классовых компонентов. - Гораздо больше интерактивных примеров и визуальных диаграмм. - В гайдах есть задачи для проверки понимания. Новая документация разделена на два раздела – обучение и справка по API. В обучении есть материал как для совсем начинающих, так и для продвинутых разработчиков. В справке по API основной упор сделан на примерах использования и возможных проблемах. https://beta.reactjs.org/

    @jhooks we only got to “99%” in the last week of december, and switchover is the top priority now that the work is picking back up after the holidays and perf reviews. the main remaining blocker is the landing page which we’re working on now

    Twitter
  • Заметки про React

    Astro 2.0 Вышла новая мажорная версия фреймворка Astro 2.0. Основные изменения: Коллекция контента. Это подход к организации контента, с помощью которого можно писать типобезопасный код в Markdown/MDX разметке. Коллекция контента – это любая папка внутри папки src/content, например “blog”, “newsletter”, "products". Внутри нее размещаются Markdown/MDX файлы с разметкой и полями. В корне src/content создается config файл, из которого экспортируется объект, где ключ - это название папки-коллекции, а значение - описание. В описании коллекции указывается Zod схема полей в разметке. После чего Astro проверяет поля в файлах разметки на правильность написания, а также предоставляет типобезопасное API запросов получения контента.
    // src/content/config.ts
    // –snip–
    const blogCollection = defineCollection({ schema: blogSchema });
    export const collections = {
      'blog': blogCollection
    };
    
    // src/pages/work.astro
    import { getCollection } from 'astro:content';
    const allBlogPosts = await getCollection('blog');
    // –snip–
    
    Гибридный рендеринг В Astro появилась возможность пререндера конкретной страницы. Эти страницы рендерятся во время сборки приложения, что приводит к экономии времени при загрузке страницы. Чтобы страница рендерилась во время сборки, нужно добавить запись:
    export const prerender = true;
    
    Другие изменения: - Редизайн страницы ошибок - Оптимизация Hot Module Reloading (HMR) - Используется Vite 4.0 https://astro.build/blog/astro-2/
    Astro 2.0 | Astro

    Astro 2.0 is here! Astro 2.0 is the first major web framework to deliver complete type-safety for Markdown and MDX. Other release highlights include: Hybrid Rendering • Redesigned Error Overlays • Improved Dev Server and HMR • Vite 4.0 • A New Public Roadmap

    Astro
  • Заметки про React

    Разбираемся в TypeScript с помощью теории множеств Владимир Клепов написал подробный гайд про устройство типов в TypeScript. Для наглядности применяются определения из теории множеств: объединение A ∪ B , пересечение ​​A ∩ B, разность A \ B , подмножество и надмножество. Например: - A extends B можно выразить как “A является подмножеством B”. - Тип объединения | и пересечения & является объединением и пересечением двух множеств. - Exclude<A, B> можно назвать разностью двух множеств, но с условием что A и B являются объединением. - never – пустое множество. A & never = never и A | never = A. Одним из интересных примеров можно выделить то, что TypeScript позволяет объявлять такие выражения: const x: {} = 9;. Это происходит из-за того, что тип {} не значит то же самое, что и пустой объект в JS. Это тип, в котором можно получить доступ к свойствам, но не важно к каким. Чтобы было понятнее, еще один похожий пример валидного выражения: const x: { toString(): string } = 9;. Это связано с тем, что TypeScript под капотом видит примитив как объект. Поэтому TypeScript позволяет выполнять такие выражения, т.к. благодаря автобоксингу JS выражение валидно и можно вызвать x.toString(). blog.thoughtspile.tech/2023/01…ipt-sets
    Making sense of TypeScript using set theory

    Vladimir Klepov as a Coder
  • Заметки про React

    Tamagui – универсальный UI kit Tamagui – это библиотека компонентов для разработчиков универсальных приложений. Есть хорошая интеграция с React Native, включая анимации интерфейса. Библиотека tamagui состоит из ядра @tamagui/core и компилятора @tamagui/static. Компилятор предназначен для генерации и оптимизации CSS стилей для веба. Поддерживает кастомизации, можно изменить тему любого компонента. https://tamagui.dev/
  • Заметки про React

    Ключ к хорошему дизайну компонента – эгоизм При разработке компонента дизайн системы можно создать пропсы, которые будут относится к содержимому компонента, а не к самому компоненту. При таком подходе можно создать слишком сложные пропсы, а бывает что при изменении дизайна и вообще придется рефакторить компонент. Главный ключ к тому, чтобы компонент был поддерживаемым – эгоизм компонента. Даниэль Ющик в статье на Smashing Magazine написал подробный гайд с примерами по созданию компонентов для дизайн системы. Автор выделил ключевые моменты, которыми нужно придерживаться при разработке компонентов: - HTML управляет дизайном компонента. При разработке абстрактного компонента, наприме, Button не стоит отклоняться от нативного поведения базового элемента. Если у нативного элемента есть проп children, то и у абстрактного он должен быть. Иначе это делает компонент более жестким к изменениям, и новым разработчикам придется менять ментальную модель для работы с данным компонентом. - children заботится сам о себе. Чем больше мы будем пытаться стилизовать содержимое компонента, тем сложнее это будет поддерживать и дорабатывать. Компонент должен стилизовать только себя, а не свое содержимое. - Используйте композицию. Некоторые компоненты, например, модальные окна могут иметь разные варианты дизайна: шапка, футер или кнопка закрытия. Вместо того, чтобы определять в едином компоненте разные варианты дизайна и активировать их через условные пропсы, разделите компонент на несколько небольших и используйте композицию:
    <Modal>
      <Modal.CloseButton />
      <Modal.Header> ... </Modal.Header>
      <Modal.Main> ... <Modal.Main>
    </Modal>
    
    www.smashingmagazine.com/2023/01…fishness
    The Key To Good Component Design Is Selfishness — Smashing Magazine

    In this article, Daniel Yuschick demonstrates that building selfish components is the key approach to avoiding different pitfalls on the way to good component design.

    Smashing Magazine
  • Заметки про React

    Почему мои Jest тесты такие медленные? Запуск тестов через Jest может занять гораздо больше времени, чем хотелось бы. Чтобы разобраться в причинах медленной работы тестов, стоит понять на что Jest тратит время. При выполнении команды запуска тестов происходит следующее: - Загрузка окружения (`jest-environment-jsdom`), построение графа зависимостей между файлами, загрузка плагинов и запуск дополнительных потоков. - Заполнение кэша. Первый запуск будет длиться чуть дольше, т.к. Jest необходимо заполнить кэш. Процесс будет идти еще дольше, если происходит транспиляция TypeScript. - Загрузка файла теста. Перед запуском теста необходимо загрузить или замокать все зависимости, указанные в файле теста и в файле setupTests.ts. Этот шаг можно оптимизировать. - Выполнение теста. Можно использовать Chrome DevTools, чтобы записать профиль производительности работы Jest и посмотреть, на что уходит больше всего времени. Автор подметил несколько основных кейсов, которые делают тесты медленными: - Баррель файлы. Если вы импортируете компонент из библиотеки, то пишите полный путь к компоненту, а не используйте баррель файл. Баррель файл – это index.ts, который реэкспортирует все экспорты. Когда Jest встречает на своем пути баррель файл, то он загружает все экспорты, объявленные в нем. - Проверьте setupTests.ts, он запускается перед каждым тестовым файлом. - Если используете TypeScript, то уберите проверку типов при запуске тестов. - Проверьте настройки Jest, возможно включены неэффективные для локальной разработки параметры запуска тестов. blog.bitsrc.io/why-is-…59bb9ac0
    Why Is My Jest Test Suite So Slow?

    The simple mistake undermining Jest’s performance

    Medium
  • Заметки про React

    Все что вам нужно знать про конкурентный рендеринг Конкурентный рендеринг в React позволяет отделить быстрые части приложения от медленных, позволяя рендерить медленные компоненты в фоновом режиме, не блокируя быстрые компоненты, чтобы каждый компонент мог реагировать на взаимодействия пользователя в своем собственном темпе. Для того чтобы активировать конкурентный рендеринг, нужно использовать либо переходы – useTransition или просто startTransition, либо отложенные значения – useDeferredValue. Эти API позволяют пометить обновления стейта низким приоритетом. Обновления стейта, которые объявляются вне фич конкурентного рендеринга, помечаются как обновления с высоким приоритетом. Хенрику Йужи подробно разобрал API конкурентного рендеринга и описал крайние кейсы использования. Например, нужно понимать, что React рендерит в однопоточной среде. И если он начал рендерить компонент, то он будет рендерить его до конца без прерываний. Только по окончании рендера компонента React проверит, есть ли какая-либо высокоприоритетная задача в пуле задач. Как следствие, следует избегать задач, интенсивно использующих ЦП, которые выполняются в одном компоненте, а не распределены по нескольким компонентам. blog.codeminer42.com/everyth…suspense
    Everything about React Concurrent Mode | The Miners

    We'll explore React Concurrent in-depth, understand what problems it solves, how it works, and how to leverage it through the use of concurrent features.

    The Miners | Codeminer42’s Engineering Blog
  • Реклама

  • Заметки про React

    Библиотеки для визуализации Подборка новых библиотек для визуализации данных для React: visx Библиотека от Airbnb, использующая под капотом D3. Состоит из множества небольших пакетов-примитивов как в D3. Поэтому при сборке приложения в конечный бандл попадет только код используемых пакетов. На сайте заявляют, что это не готовая библиотека для построения графиков. С помощью этой библиотеки вы можете самостоятельно сделать свою библиотеку для построения графиков.
    export default function Bars() {
      // --snip--
      const yScale = useMemo(
        () =>
          scaleLinear<number>({
            range: [yMax, 0],
            round: true,
            domain: [0, Math.max(...data.map(getLetterFrequency))],
          }),
        [yMax],
      );
    
      return (
        <svg width={width} height={height}>
          <GradientTealBlue id="teal" />
          <Group top={verticalMargin / 2}>
            {data.map((d) => {
              // --snip--
              const barHeight = yMax - (yScale(getLetterFrequency(d)) ?? 0);
              const barX = xScale(letter);
              return (
                <Bar
                  key={`bar-${letter}`}
                  x={barX}
                  y={barY}
                 // --snip--
                />
              );
            })}
          </Group>
        </svg>
      );
    }
    mafs Библиотека готовых компонентов для интерактивной математики. Позволяет создавать интерактивные и анимированные элементы из математики. Содержит компоненты: система координат, точки, линии, полигоны, круги и т.д.
    function LineSegmentExample() {
      const point1 = useMovablePoint([-1, -1])
      const point2 = useMovablePoint([2, 1])
    
      return (
        <Mafs viewBox={{ y: [-1, 1] }}>
          <CartesianCoordinates />
          <Line.Segment
            point1={point1.point}
            point2={point2.point}
          />
          {point1.element}
          {point2.element}
        </Mafs>
      )
    }
    visx

    a collection of expressive, low-level visualization primitives for React

    airbnb.io
  • Заметки про React

    Практики композиции функций в React Композиция функций – мощный способ создания сложной функциональности путем объединения простых и переиспользуемых функций. В контексте React композиция функций позволяет создавать более сложные и переиспользуемые компоненты, что улучшает организацию и структуру кода в больших приложениях. Филиппо Ривольта в своем блоге поделился практиками композиции функций: - Используйте HoC для абстракции общей функциональности. Оборачивая в HoC мы можем добавить дополнительное поведение или пропсы, не изменяя оригинальный компонент. - Объединяйте несколько HoC, используя композицию: compose(withFoo, withBar, withBaz)(Component). - Один HoC делает только одну работу, используйте кастомные хуки для хранения состояния. - Используйте техники функционального программирования, например, каррирование. Пример реализации HoC, который абстрагирует логику получения данных о пользователе:
    import { useEffect, useState } from 'react';
    
    function useFetchUsers(url) {
      const [users, setUsers] = useState([]);
      useEffect(() => {
        async function fetchData() {
          const res = await fetch(url);
          const data = await res.json();
          setUsers(data.users);
        }
        fetchData();
      }, [url]);
      return users;
    }
    
    function withFetchUsers(Component) {
      return function WrappedComponent(props) {
        const users = useFetchUsers(props.url);
        return <Component {...props} users={users} />;
      }
    }
    
    medium.com/@rivolt…ba8d37b1
    Function Composition in Large React Applications: Best Practices and Real-World Examples

    Learn how to use function composition in large React applications to create more sophisticated and reusable components. Discover best…

    Medium
  • Заметки про React

    Абсолютная абстракция форм в React Гайд по созданию абстракций для форм от Брендана Аллана. В примерах для работы с формами автор использует react-hook-form, TypeScript и Zod. Преимущество Zod в том, что он полностью поддерживает TypeScript, также есть утилитарный тип TypeOf для выведения типа из схемы. RHF поддерживает Zod через resolver. Пример кастомного хука формы:
    import { useForm, UseFormProps } from 'react-hook-form';
    import { zodResolver } from '@hookform/resolvers/zod';
    import { TypeOf, ZodSchema } from 'zod';
    
    interface UseZodFormProps<Z extends ZodSchema>
      extends Exclude<UseFormProps<TypeOf<Z>>, 'resolver'> {
      schema: Z;
    }
    
    export const useZodForm = <Z extends ZodSchema>({
      schema,
      ...formProps
    }: UseZodFormProps<Z>) =>
      useForm({
        ...formProps,
        resolver: zodResolver(schema),
      });
    
    www.brendonovich.dev/blog/th…traction
    The Ultimate Form Abstraction

    Typesafe forms in React? Sounds like a job for React Hook Form and Zod!

    Brendonovich