<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Guillaume Renard on Medium]]></title>
        <description><![CDATA[Stories by Guillaume Renard on Medium]]></description>
        <link>https://medium.com/@gfox1984?source=rss-c0c83930ac5f------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/1*Z92_2NoGRRUUkQhbVIi5fg.jpeg</url>
            <title>Stories by Guillaume Renard on Medium</title>
            <link>https://medium.com/@gfox1984?source=rss-c0c83930ac5f------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 20 Jun 2026 05:58:57 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@gfox1984/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[Should frontend developers go full-stack (again)?]]></title>
            <link>https://medium.com/@gfox1984/should-frontend-developers-go-full-stack-again-a9db01639c63?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/a9db01639c63</guid>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[careers]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[frontend]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Wed, 14 May 2025 17:23:05 GMT</pubDate>
            <atom:updated>2025-05-14T17:23:05.211Z</atom:updated>
            <content:encoded><![CDATA[<h4>In an era dominated by client/server applications, many developers have chosen between the front and the back end. It might be time to rethink this approach.</h4><figure><img alt="A man is standing inside a large dotted circle. He holds his chin in an inquisitive gesture." src="https://cdn-images-1.medium.com/max/1024/0*A2if36EFpVimcg6s.png" /><figcaption>Image generated with <a href="https://copilot.microsoft.com/shares/eKXUpZXLumLLASvSDMucx">Microsoft Copilot</a></figcaption></figure><p>As a millennial, I started coding for the web early. I used to write static HTML pages and host them for free on an Apache server. But copy-pasting the same markup over and over was tedious... If only there were a better way! A tool you could use to generate pages from actual content, <em>dynamically</em>. Say hello to PHP and MySQL!</p><p>Shortly after, I wanted visitors to post comments and upload content too, so I rolled my own auth… storing passwords in clear text (of course). And since I hadn’t discovered the concept of sessions, I would transfer the same clear-text password and username back to each page via the URL so the users remained “signed in”. How fun?</p><p>Without knowing it, I had become a full-stack developer (not that the term existed yet… maybe a <em>webmaster</em>?). A bad one, but trying!</p><p>I love the process of facing a problem, working through it, and reaching the limits. Only then can one truly appreciate the effort others have put into solving the same problem, often in the most elegant ways.</p><blockquote>There is something unique and exciting about the web</blockquote><p>Back then, heavy desktop applications were still king. Executables could achieve much more than the web in terms of user experience. I dreamed of creating such programs, and it took me a long time to learn those skills…</p><p>But while building for the desktop was fun, something was missing. The truth is, there is something unique and exciting about the web that heavy desktop applications lack. With a website, you can connect with anyone in just a click, regardless of the hardware, operating system, or location. Updating content, rolling out new features, or fixing bugs happens instantly.</p><p>After graduating in 2007, I started working at a software company that sold both web and desktop applications. At that time, there was still a lot of space for the two to coexist. But eventually, JavaScript became good enough, and we converted most of our heavy clients to web applications. So I became a full-stack developer (again), writing SQL, implementing web services, configuring authentication, integrating with a framework, fetching data from the page, and rendering it.</p><p>We built the frontend using ASP.NET components, avoiding spaghetti jQuery as much as possible. We also became quite good at using the DOM and reducing network requests. However, managing and synchronizing state on complex pages was always a headache.</p><p>So, when React landed, it was love at first sight! React solved real-world problems. It had the same component approach we loved, and it removed a lot of complexity.</p><blockquote>So I became the frontend guy, and I loved it!</blockquote><p>I ultimately lost all interest in backend development. With the way the web was going, it became clear that companies needed specialized engineers who could master all those new libraries, avoid their pitfalls, and actively stay updated. So I became the frontend guy, and I loved it! The same can probably be said of backend developers who abandoned all frontend development and truly embraced serverless architecture and its infrastructure.</p><p>But the wind is changing again.</p><p>Not only the separation between the back and the front end has created silos and boundaries between the two worlds, it has become apparent that it also came at the expense of performance and simplicity.</p><p>As I’m writing this, the client often has to bend to deal with whatever the API endpoints offer — usually a poor compromise between how the server structures its data and how the client would like to consume it. This leaves both back and front-end developers frustrated.</p><p>As a result, we make too many requests, fetch too much data, and put extra load on the client and the server. We implement caching to reduce round-trips, which in turn causes new problems, such as cache invalidation. We implement optimistic updates to hide issues with latency and pray that nothing ever goes wrong.</p><blockquote>To reconcile the back and the front ends</blockquote><p>And so, developers have started looking for ways to reconcile the back and the front ends.</p><p>Modern frameworks such as <a href="https://reactrouter.com/home">React Router 7</a> or <a href="https://tanstack.com/start/latest">TanStack Start</a> let server and client code sit next to each other, while taking care of the plumbing. They also make pre-loading, caching, invalidation, and optimistic updates easier.</p><p>React Server Components (RSC) take this one step further by running your components on the server (or the compiler) by default, with a simple way to opt out when your component needs interactivity or local state (“use client”).</p><p>And if this wasn’t enough, sync engines, such as <a href="https://zero.rocicorp.dev/">Zero</a>, now allow frontend developers to code as if everything were local by creating a replica of the DB on the client. The engine itself handles the synchronization with the server, removing most of the complexity introduced by the network boundary, which is truly amazing!</p><blockquote>Shipping a full project on your own is not as easy as it seems</blockquote><p>But if you’ve been focusing solely on writing frontend code and consuming APIs written by others lately, you’ll quickly realise that shipping a full project on your own is not as easy as it seems.</p><p>Yes, there are plenty of resources and solutions for hosting your app. You can go serverless, you can use an all-in-one hosting solution, or you can host it yourself on a VPS. But how do you actually choose one?</p><p>What about the database? Which one will you pick, and what’s the cost of running it? Plus, how do you back it up and secure it?</p><p>At some point, you will also want to have users… and authorizations… So, will you roll your own auth, find a library that works with your stack, or integrate with a 3rd party provider?</p><p>Those are just a few random questions you will have to answer and problems you will have to solve before you can ship anything (I spare you the CI/CD part).</p><p>If you’ve been working as a frontend engineer in a medium or large company, chances are you’ve delegated those tasks to others to focus on the part you were good at instead…</p><blockquote>Technology should aim to blur the line between the back and the front end</blockquote><p>But as technology shifts and development resources shrink (hello AI), there will be incentives for web development to simplify and ship faster. Multi-tier applications, micro-service architecture, strict separation between the client and the server, and other trends have all failed to achieve this. They may have solved a few developer problems, but not consumer ones.</p><p>Software development should be more about solving business problems than fixing issues that result from our technical decisions. Whether you use a full-stack framework, RSC, or a sync engine, back and front should work hand in hand. The technology should aim to blur the line between the two and reduce the network to an implementation detail.</p><p>When I heard about sync engines, I decided to try <a href="https://zero.rocicorp.dev/">Zero</a>. I had no trouble running it locally, and I really enjoyed the developer experience and the speed of the app. I saw the future in it. But when I wanted to host my project, I realized that I knew little about AWS (the preferred hosting solution), and even less about Postgres (the only supported database). I looked at other cloud providers (GCP), but was not able to go very far. I was also shocked at the cost of running a database!</p><blockquote>I have fallen behind by letting others at my job handle the server code, persistence, hosting, and auth.</blockquote><p>Unfortunately, I have fallen behind by letting others at my job handle the server code, persistence, hosting, and auth. I am no longer up-to-date, and I have to learn how to run a DB, host an app, and set up auth again… the 2025-way.</p><p>Fortunately for me, it is now possible to write a full-fledged app with mostly frontend code, and a few additional functions on the server (think manipulating data or checking authorizations). The rest is mainly about discovering and configuring the tools to support it.</p><p>So, being a full-stack developer no longer means spending:</p><ul><li>40% on the frontend,</li><li>40% on the backend,</li><li>20% on the rest (DB, tooling, infra)</li></ul><p>But more like:</p><ul><li>60% on the frontend</li><li>10% on the backend (which happens to be JavaScript)</li><li>30% on the rest (DB, tooling, infra)</li></ul><blockquote>Frontend developers are going to need some new skills</blockquote><p>I don’t think all specialized backend or frontend developers will become obsolete overnight, as the solutions mentioned here are still fairly new. The industry has a lot of inertia, and it’s still as hard as ever to find good developers, specialized or not.</p><p>Frontend-oriented frameworks definitely won’t fit all. Large applications will keep complex logic and heavy processes on the backend. But in any case, we should stop throwing RESTful(<em>-ish</em>) APIs to the frontend. Instead, the frontend code should run closer to the source: either on the server, using RSC or another framework, or stay on the client, provided the data is synced there. No matter what, frontend developers are going to need some new skills…</p><p>So looks like we’ve gone full circle and the world is calling for full-stack developers… again 🙌</p><p>If you’re still here, check this out and see which full-stack developer you want to be!</p><ul><li><a href="https://tanstack.com/start/latest">TanStack Start</a> &amp; <a href="https://reactrouter.com/home">React Router 7</a></li><li><a href="https://overreacted.io/impossible-components/">Impossible Components</a> &amp; <a href="https://www.youtube.com/watch?v=RBph3T6Tqxo&amp;ab_channel=LogRocket">JSX Over The Wire on YouTube</a> (RSC)</li><li><a href="https://www.youtube.com/watch?v=SNAHZZo21To&amp;ab_channel=Zero">Making React Fun Again with Sync</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a9db01639c63" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[The Testing Burger ]]></title>
            <link>https://levelup.gitconnected.com/the-testing-burger-c2f195144865?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/c2f195144865</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[testing]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[ux]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Tue, 05 Nov 2024 17:41:18 GMT</pubDate>
            <atom:updated>2024-11-05T17:41:18.943Z</atom:updated>
            <content:encoded><![CDATA[<h4>Because web apps crave a new testing strategy.</h4><figure><img alt="A burger on a white background" src="https://cdn-images-1.medium.com/proxy/0*ElowkiKeyjqzqHQP" /><figcaption>Image generated with <a href="https://copilot.microsoft.com/images/create/a-burger-in-claymation-with-white-background2c-noth/1-67078d8d03434613bc0ca3dc235d5cdf?id=4bGGHVuQhBTh48loII1Hkw%3D%3D&amp;view=detailv2&amp;idpp=genimg&amp;thId=OIG2.x_SIrQNuTE56vY61PAqu&amp;skey=C700WbEF5Z7rEkC6L5cU7AJCC3_9hyxjSf8FbnZ3fX4&amp;FORM=GCREDN&amp;mode=overlay">Microsoft Copilot</a></figcaption></figure><h3>Why?</h3><p>I like testing web apps. However, I came to the conclusion that the way we test them does not scale. So I recently embarked on a journey toward better frontend testing:</p><p><a href="https://levelup.gitconnected.com/are-playwright-and-vitest-ready-to-replace-jest-3a52f03ee03c">Are Playwright and Vitest ready to replace Jest?</a></p><p>After trying out a few things, I settled on the following strategy:</p><ol><li>Write end-to-end (E2E) tests at route level to safeguard my acceptance criteria.</li><li>Write stories to test my components with different use cases.</li><li>Write regular unit tests for the rest (mostly utility functions).</li></ol><p>You may have heard of the <a href="https://martinfowler.com/bliki/TestPyramid.html">testing pyramid</a> or the <a href="https://kentcdodds.com/blog/the-testing-trophy-and-testing-classifications">testing trophy</a> before. These metaphors tell you where to invest your time and resources when testing. They give you an idea of the proportion of tests to aim for in each category (unit, integration, and end-to-end tests).</p><p>I have mixed feelings about that.</p><p>First, the boundary between each kind of test is blurry. People have different definitions for unit, integration, and E2E tests, especially in web applications.</p><p>Second, the testing pyramid is old. It was built on the assumption that E2E tests were slow, unreliable, and high-maintenance. This is no longer accurate. Tools have evolved and frontend developers can now build all kinds of tests directly into their apps. And thanks to the influence of <a href="https://testing-library.com/">Testing Library</a>, these E2E and integration tests are also more resilient and easier to maintain than they used to be.</p><p>Third, these classifications don’t tell you how to get there. What shall you test? How? The proportion of tests, just like code coverage, is a consequence of your testing strategy. It’s not an end. You don’t test to reach a number.</p><p>Remember <a href="https://en.wikipedia.org/wiki/Goodhart%27s_law">Goodhart’s law</a>?</p><blockquote>When a measure becomes a target, it ceases to be a good measure.</blockquote><p>When a framework focuses on achieving a target percentage, it ultimately distracts people from their original objective, which is to have useful and meaningful tests.</p><p>So forget the testing pyramid and the trophy; let’s go for a burger instead 🍔!</p><p>Not a burger that will tell you how many E2E, integration, or unit tests to write —the result might look like a trophy, a square… or a blob. I don’t care. What I care about are the what and why. So, this burger will be a metaphor for an actual testing strategy instead.</p><p>But enough talking, let’s bite into it.</p><h3>How do you test a burger?</h3><h4>1. Does it taste like a burger?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*xm_1jPwFY2wZvtRj" /><figcaption>Photo by <a href="https://unsplash.com/@marcelheil?utm_source=medium&amp;utm_medium=referral">Marcel Heil</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Take a big bite of your burger:</p><ul><li>Does it taste like a burger?</li><li>Does it have the right consistency?</li><li>Is it yummy?</li></ul><p>Then you can confidently claim that you have a good burger.</p><p>But how does this translate to your app? Well, those big bites are end-to-end tests. They are <em>vertical slices</em> that cut through your entire app.</p><p>Use E2E tests to ensure your app meets acceptance criteria and performs as it should. In short, verify that your app delivers what you sold your customers.</p><p>For example, if your app is a TODO list, write E2E tests that verify that:</p><ol><li>Users can add items</li><li>Users can complete items</li><li>Users can delete items</li></ol><p>But that’s not enough, what about the ingredients?</p><h4>2. Does it have the right ingredients?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*TWly1T00-JGUOreL" /><figcaption>Photo by <a href="https://unsplash.com/@pablomerchanm?utm_source=medium&amp;utm_medium=referral">Pablo Merchán Montes</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>If you pick each ingredient individually, do they live up to expectations?</p><ul><li>Is your salad the right size? Is it crunchy?</li><li>Is your patty the right weight? Does it have the right amount of fat?</li><li>How’s your tomato? Not too thin, not too thick?</li></ul><p>This will tell you whether your ingredients meet the standards.</p><p>It also means you can change suppliers or improve the preparation, provided the ingredients still pass the tests.</p><p>And with those A-grade ingredients, you can make other quality burgers!</p><p>But what about your app? Testing ingredients is equivalent to testing components (= <em>horizontal slices</em>). Make sure your button performs as a button or your list renders the given items.</p><p>For example, your TODO app may have the following component tests:</p><ol><li>The Card component renders a title, an optional description, and a checkbox</li><li>The checkbox toggles its state when clicked</li><li>The list renders the given items in the expected state</li></ol><p>Components tests alongside E2E tests let you cross-test your app and ship with confidence.</p><p>But there’s one more thing…</p><h4>3. Is your burger done right?</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*heN71-Jq4ZnvHagW" /><figcaption>Photo by <a href="https://unsplash.com/@zaccain?utm_source=medium&amp;utm_medium=referral">Zac Cain</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><p>Last but not least, you might also want to check that you’re making burgers the right way:</p><ul><li>Are the ingredients stacked in the correct order?</li><li>Is the sauce applied evenly?</li><li>How do you bake your buns?</li></ul><p>Any failure at the preparation level will most certainly affect the overall quality, aspect, or taste of the burger. So aren’t these tests redundant?</p><p>Probably, but they’ll help you identify issues faster than tasting the whole burger. Where did the salt get swapped with sugar? If you test your mayo recipe, you’ll know.</p><p>In your app, this means testing utility functions, processes, algorithms, and logic using regular unit tests. Provide inputs to the tested function and verify its output.</p><p>Your TODO app may not have many utility functions, but you could test the one that sorts items according to the creation date for instance.</p><p>This, folks, is the Testing Burger.</p><blockquote>Vertical slices (E2E tests) + horizontal slices (component tests) + logic (unit tests) = Testing Burger 🍔</blockquote><p>Before I wrap this up and ring the service bell️, I want to talk about the bun and why it matters.</p><h3>So what about the bun?</h3><p>The bun is the interface between your fingers and the burger. A bun has two halves, meaning two interfaces. Similarly, web apps have two interfaces: the underlying network and the browser.</p><p>I’m mentioning this because those two interfaces have historically been mocked in testing… with more or less success.</p><p>Not long ago, developers went to great lengths to mock the fetch API. But with the rise of tools such as <a href="https://mswjs.io/">Mock Service Worker</a> (MSW), we can now use the actual bun. MSW simply simulates the paper around the burger, but the burger inside is real.</p><p>And what about the top bun or <em>browser</em>? In web apps, developers haven’t been able to use the browser for testing until very recently. It was either too slow, too unreliable, or it demanded too much infrastructure. So they used a fake bun instead: <a href="https://github.com/jsdom/jsdom">JSDOM</a>. Sure it looked like a bun, but when you bit your burger, you could tell it wasn’t real: you had a hard time chewing and it tasted like Play-Doh.</p><p>However, developers are starting to use <a href="https://playwright.dev/">Playwright</a> to test their apps in an actual browser. And <a href="https://vitest.dev/guide/browser/">Vitest browser mode</a>, which is still in development, is making in-browser testing even easier. Tools such as <a href="https://storybook.js.org/">Storybook</a> also started embracing it to implement first-class component testing.</p><p>So we no longer have to fake the bun.</p><h3>Your order is ready</h3><p>Okay, E2E tests have already been around for a while (well, maybe not the ones that mock the network) and I’m sure you’ve tested components before too (even if called differently). So nothing is technically new here.</p><p>But before the rise of modern tooling, cross-testing your app would have been more painful and slower than it should be, and implementing the Testing Burger wouldn’t have made much sense.</p><p>I have huge respect for Martin Fowler and Kent C Dodds, who invented the test pyramid and the testing trophy respectively. If people started testing their apps, it’s mostly thanks to them. And if you listen to Kent, you’ll see that what he advocates resembles the Testing Burger. I just feel that these classifications are taking the problem from the wrong end and that the talk should be recentered on the actual testing strategy.</p><p>So if you’re a frontend developer, I hope the analogy speaks to you as much as it speaks to me. But if not, I hope it opened your appetite for testing 🤤</p><p><em>Hey! I’m Guillaume, a longtime frontend engineer. I want you to know that I will be writing a follow-up article with the actual, concrete, and step-by-step setup for implementing the Testing Burger. So if you’re looking forward to it, hit ‘</em><strong><em>Follow</em></strong><em>’ and stay tuned!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=c2f195144865" width="1" height="1" alt=""><hr><p><a href="https://levelup.gitconnected.com/the-testing-burger-c2f195144865">The Testing Burger 🍔</a> was originally published in <a href="https://levelup.gitconnected.com">Level Up Coding</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Are Playwright and Vitest ready to replace Jest?]]></title>
            <link>https://levelup.gitconnected.com/are-playwright-and-vitest-ready-to-replace-jest-3a52f03ee03c?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/3a52f03ee03c</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-development]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[testing]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Tue, 08 Oct 2024 16:38:39 GMT</pubDate>
            <atom:updated>2024-12-04T10:00:13.763Z</atom:updated>
            <content:encoded><![CDATA[<h4>My journey towards better frontend testing.</h4><figure><img alt="Image of a man scrutinizing a tablet with a magnifying glass, standing while the tablet rests on a white cube, all in a claymation style with a light blue sky background." src="https://cdn-images-1.medium.com/max/1024/1*zugItdfP3SVM2i7vDh8-mw.png" /><figcaption>Image generated with <a href="https://copilot.microsoft.com/images/create/a-man-scrutinizing-a-tablet-with-a-magnifying-glas/1-6703edeb0120404c899406ddd3d37ef7?id=rLcLRJUwIBaYH4efREmfMw%3d%3d&amp;view=detailv2&amp;idpp=genimg&amp;idpclose=1&amp;thId=OIG2.cZKj8J4oFTxbciOSUEuc&amp;lng=en-GB&amp;ineditshare=1">Microsoft Copilot</a></figcaption></figure><h3>What is wrong with frontend testing?</h3><p>When you look at tutorials (including mine), everything is always nice and easy when it comes to testing an app, because:</p><ol><li>it is a fresh install</li><li>it uses modern tooling</li><li>it has few dependencies</li><li>there are only a handful of tests</li></ol><p>But when you try to test one of your existing projects, you have to deal with the following:</p><ol><li>it was created with Webpack or Create-React-App (CRA)</li><li>the tools haven’t been updated in years</li><li>it has many dependencies, some of which are barely maintained</li><li>it already has some tests, with variable quality, questionable relevance, and an overall lack of strategy</li></ol><p>I work on these projects—mostly enterprise software, with a fair share of technical debt. I have been using the tools available at the time and following the best practices highlighted in the <a href="https://testing-library.com/docs/guiding-principles">Guiding Principles of Testing Library</a>. And I have mostly enjoyed testing so far.</p><p>But I came to the conclusion that testing, in its current form, doesn’t scale. There is always a point where:</p><ul><li>tests are running slow and timeouts are becoming the norm</li><li>it is common for code that works in the browser to fail in the test suite</li><li>some portions of code rely so much on mocks that the tests lose relevance</li><li>debugging without “seeing” is extremely painful</li><li>some dependencies will break in the tests, with some obscure error (usually an issue with CJS vs ESM modules)</li></ul><p>So what can we do about this? How can we enhance the developer experience (DX) and our tests at the same time?</p><p>Let’s explore a few options.</p><h3>Jest</h3><figure><img alt="Screenshot showing a failed test in Jest. Expected is “Testing with Jest is good for you”, received is “Testing your luck is bad for you”" src="https://cdn-images-1.medium.com/max/1024/0*IPMhRYdO5byb1cVr.png" /><figcaption>Image from <a href="https://jestjs.io/">Jest · 🃏 Delightful JavaScript Testing (jestjs.io)</a></figcaption></figure><p><a href="https://jestjs.io/">Jest</a> is a testing framework developed by Facebook. It aims to provide a fast, simple, zero-config, and reliable way to test web applications. If the Jest API (test, expect, beforeEach, etc.) has stood the test of time (10+ years!), I’m not so sure it fully delivered on its promises… In 2024, Jest is neither fast nor easy to set up!</p><p>The other culprit, <a href="https://github.com/jsdom/jsdom">JSDOM</a>, comes from the same era as Jest. JSDOM is a fake DOM implementation that you use to render components inside the test runner (= in Node, instead of the browser). Unfortunately, the implementation is partial and performance is poor by today’s standards.</p><p>Jest’s last major release was two years ago and the tool only has experimental support for ECMAScript Modules (the standard module system). JSDOM is barely maintained and the most downloaded versions (20.0.3 and 16.7.0) are over 2 years old.</p><p>And if you’re still using Jest through <a href="https://create-react-app.dev/">Create-React-App (CRA)</a>, I have bad news for you: you’re probably running versions of Jest and JSDOM that are over 3 years old 🙈.</p><p>Sure, you could always update Jest and JSDOM to the latest version and delay the inevitable… Even better, you could replace JSDOM with <a href="https://github.com/capricorn86/happy-dom">Happy DOM</a> — another DOM implementation that is actively maintained and aims for better performance. The migration is pretty straightforward: replace one dependency (jsdom) with another (happy-dom), maybe fix a few edge cases, and off you go.</p><p>But based on my experience, you will only achieve marginal gains this way. The pain points I highlighted earlier will still exist.</p><p>Also, because of issues with third-party dependency, I’m willing to bet that you are currently using Jest’s ‘magic’ setting in your legacy projects: transformIgnorePatterns. The setting allows Jest to transpile problematic modules to a format it understands (CommonJS). While this mostly works, it is also terribly slow: the transformation happens before running the tests, which may take several seconds or even minutes.</p><p>OK, so what about Jest’s main competitor: Vitest?</p><h3>Vitest</h3><figure><img alt="Screenshot of Vitest UI showing a failed test" src="https://cdn-images-1.medium.com/max/1024/0*2yel3UsTqgByA31e.png" /><figcaption>Image from <a href="https://vitest.dev/guide/ui.html">Vitest UI | Guide | Vitest</a></figcaption></figure><p>As the name suggests, <a href="https://vitest.dev/">Vitest</a> is another testing framework that is part of the <a href="https://vite.dev/">Vite</a> ecosystem. Its API is very close to Jest, making it easy to migrate to Vitest without rewriting test cases. And just like Vite, Vitest can run straight from your sources (with minimal transformation), making it pretty fast to kick in.</p><p>Unlike Jest, Vitest uses ESM, the latest standard for JavaScript modules. While this works great with new projects, Vitest can be picky with your imports. Packages that are bundled improperly will cost you hours looking for solutions. You might have to use some workarounds that are either slow or inconvenient, such as <a href="https://vitest.dev/config/#server-deps-inline">inlining dependencies</a>…</p><p>While Vitest is a step forward compared with Jest, you shouldn’t expect too much from it alone. Even if you manage to run it on a legacy project, you will probably not see a big performance boost. Testing times should improve (provided you didn’t inline too many dependencies…), but only marginally. And the developer experience will broadly be the same.</p><p>I’m afraid, testing web apps with a fake DOM simply doesn’t scale, no matter the tool. Mocking is a problem. Performance is a problem. Testing without seeing is also a problem.</p><p>So, what are the other options?</p><h3>Playwright Component Tests</h3><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fy3YxX4sFJbM%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dy3YxX4sFJbM&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fy3YxX4sFJbM%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/1a358000feb17ebcf3d4922a7a32f97d/href">https://medium.com/media/1a358000feb17ebcf3d4922a7a32f97d/href</a></iframe><p>When <a href="https://playwright.dev/">Playwright</a> announced <a href="https://playwright.dev/docs/test-components">component testing</a> I started to get excited. The promise was to write tests just like with Jest and <a href="https://testing-library.com/">Testing Library</a>, but have them run inside a real browser, instead of some fake DOM.</p><p>Problem solved? Well, not so fast.</p><p>Playwright component testing suffers from <a href="https://github.com/microsoft/playwright/issues/23662">CSJ/ESM module hell</a>, meaning you’ll have a hard time getting it running on a legacy project (unless the project has zero dependencies or all dependencies are perfectly bundled…).</p><p>And if you manage to get through this, you won’t be able to mock the network with <a href="https://mswjs.io/">Mock Service Worker (MSW)</a> since it is <a href="https://github.com/microsoft/playwright/issues/30981">not natively supported in component tests</a>. You can still <a href="https://playwright.dev/docs/next/test-components#handling-network-requests">import MSW handlers via Playwright’s router</a> as a workaround, but MSW won’t be available in your tests. This is such a letdown considering that MSW is one of the few tools developers are not looking to replace!</p><p>I hope the situation improves but for now, Playwright component tests are not an option.</p><h3>Playwright (E2E)</h3><figure><img alt="Screenshot showing the timeline feature of Playwright UI" src="https://cdn-images-1.medium.com/max/1024/0*XPkOqZfhZeOROezq" /><figcaption>Image from <a href="https://playwright.dev/docs/test-ui-mode">UI Mode | Playwright</a></figcaption></figure><p>So what about using <a href="https://playwright.dev/">Playwright</a> directly, <strong>without</strong> component tests? I mean: use the Playwright API to open a page and write assertions against it.</p><p>My initial thought was: “Yuck! Playwright is for end-to-end testing (E2E). These tests sure have a place in the <a href="https://martinfowler.com/articles/practical-test-pyramid.html">testing pyramid</a>, but they should stay out of my app”.</p><p>Past the disgust, I decided to give it a try. But with one constraint: the tests should not hit an external system or service. They should be self-contained.</p><p>One of the advantages of using Playwright directly is that whatever runs in the browser runs in your tests. So unlike in component tests, we can use MSW to mock the network requests. This way, we can write predictable tests that don’t have any side effects, while working with the real app.</p><p>I found one caveat though: mocking authentication (eg OAuth) is hard, due to the nature and the complexity of the interactions involved. You can solve this by injecting a token in the page with Playwright (eg: by setting a global variable) and pass it to your auth library. This means creating a special setup (or configuration) for your app to run inside Playwright. There isn’t a magic recipe for this: use whatever works for you and your special requirements.</p><p>This was the tedious part.</p><p>The rest is pure joy! Once your app is set up, you can load routes, interact with the page, and write assertions. And the best part: you can see what’s happening in real time! The Playwright UI is a delight. It lets you access the console, view the network requests, and inspect the exact state of the page at any time, so it’s easy to debug when your assertions are failing. Oh boy, I was not ready for this!</p><p>So is Playwright <strong>THE </strong>solution?</p><p>Unfortunately no. It may be part of it, but it is not the ultimate solution. E2E tests are slow, even when mocking the network. Imagine refreshing the page after each test case! Sure, you could always bend the rules and load each page once, then run multiple test cases, with the risk of breaking isolation. I’m not sure I’m very comfortable with the idea yet.</p><p>Also testing solely at the route level is a no-go. I want to test components in isolation too, which is not possible with Playwright — the very issue component tests were supposed to address... If you want to test components individually, you first need to install a tool such as <a href="https://storybook.js.org/">Storybook</a> or <a href="https://ladle.dev/">Ladle</a>, then write stories for them that you can use in the browser.</p><p>I did not like the thought of using another tool initially, so I tried writing a few custom routes by hand for each component, which I then tested inside Playwright. While the approach worked, I felt I was reinventing Storybook… without the DX.</p><p>So why not use Storybook instead?</p><h3>Storybook Component Tests</h3><figure><img alt="Video of Storybook GUI showing two component stories for a form and an interaction test." src="https://cdn-images-1.medium.com/max/800/1*yk-iBB6kolFnpmKMxV3nlQ.gif" /><figcaption>Video from <a href="https://storybook.js.org/docs/writing-tests/component-testing">Docs | Storybook</a></figcaption></figure><p><a href="https://storybook.js.org/">Storybook</a> is a tool used to build UI components. Whenever you write a new component, Storybook lets you add stories (= small snippets) in a file next to it, which you can then visualize in the browser. This allows developers to test their components through different use cases with instant feedback.</p><p>Although my overall experience with Storybook has been positive, some developers have expressed concerns that Storybook was bloated (too many dependencies). Luckily, the latest version (8.3 to date) addresses this, so this is no longer a fair thing to say. The tool has consistently improved and become faster since version 7.</p><p>It’s also been a while since Storybook understood that the future was with browser testing, not fake DOM. As it turns out, Storybook also comes with a test runner, which uses Playwright internally.</p><p>Component testing in Storybook consists of running your stories in a browser and verifying they render without errors, giving you some code coverage for free!</p><p>But stories alone can’t replace regular unit tests. So, Storybook lets you attach a play function to them, which you can use to write assertions and interact with the component. The API looks very familiar to Jest and Testing Library. This allows you to play and replay tests step by step within Storybook and run them in a test runner.</p><p>One source of truth (the story), many outputs! That’s what I call good DX.</p><p>That being said, running the test runner can be problematic in practice. The test runner requires Storybook to be up and running beforehand, which is not ideal in a CI pipeline or a pre-commit hook…</p><p>For this reason, I never regarded Storybook as a serious testing option... until the introduction of the new experimental <a href="https://storybook.js.org/docs/writing-tests/vitest-plugin">Vitest plugin</a>! This plugin substitutes the test runner with Vitest and uses <strong>browser mode</strong> to execute tests, doing all the heavy lifting for you.</p><p>Despite being in the early stages, it already works remarkably well!</p><p>But what is Vitest browser mode exactly?</p><h3>Vitest browser mode</h3><figure><img alt="Screenshot of Vitest UI in browser mode showing a few passing tests, as well as the browser UI." src="https://cdn-images-1.medium.com/max/1024/0*-A3j-gsUL3QeUKwO.png" /><figcaption>Image from <a href="https://vitest.dev/guide/browser/">Browser Mode | Guide | Vitest</a></figcaption></figure><p>Vitest <a href="https://vitest.dev/guide/browser/">browser mode</a> is similar to Playwright <a href="https://playwright.dev/docs/test-components">component tests</a>. It has the same API as regular tests but renders in the browser thanks to an E2E provider — typically <a href="https://playwright.dev/">Playwright</a>. Unlike Playwright component testing, it’s easy to get running and you can use <a href="https://mswjs.io/">MSW</a> in your tests!</p><p>With <a href="https://vitest.dev/guide/workspace.html">Vitest Workspaces</a> you can also run browser tests alongside regular unit tests. This makes it very convenient to collect code coverage from both sources. Because, yes, Vitest can collect coverage metrics in browser mode too — something that is hard to achieve with Playwright (see <a href="https://github.com/microsoft/playwright/issues/7030#issuecomment-1575606073">My Playwright code coverage journey</a>). This might not seem like a big deal, but many companies require code coverage reports to be produced for all apps (often for the wrong reasons, but that’s another story).</p><p>So oddly enough, there is not much to say about Vitest browser mode: it’s simple, it’s fast, and it just works! Using browser mode does not require new knowledge. A bit of config is all it takes. Sure, it’s still experimental, but you can already see the potential.</p><h3>My ideal setup… for 2025</h3><p>Alright! This has been a long and tiring journey, but I have gathered enough information to identify where I want to head with testing now.</p><p>My ultimate objectives are:</p><ol><li>to invest less time in testing</li><li>to get more coverage out of it</li><li>to have fast and reliable tests</li></ol><p>So here is my strategy to achieve this:</p><ol><li>write E2E tests at route levels with Playwright to safeguard my acceptance criteria</li><li>write stories in Storybook to test my components with different use cases, and run them using Vitest browser mode</li><li>write regular unit tests with Vitest for the rest (mostly utility functions)</li></ol><p>This is what I call: <a href="https://medium.com/gitconnected/the-testing-burger-c2f195144865">the Testing Burger 🍔</a>.</p><p>It’s worth noting that Vitest browser mode could be used to achieve point 1, but I prefer to use Playwright because it runs against the actual application, which gives me more confidence in the tests. Playwright’s GUI also offers a better developer experience than Vitest’s.</p><p>You could also use Vitest browser mode for component testing (point 2) but Storybook is well worth the extra overhead in my opinion. By leveraging the power of Storybook, you can develop components in isolation and test many use cases without breaking a sweat. Add a play function and some assertions to your stories and you no longer need regular unit tests.</p><h3>For now…</h3><p>The future I describe above is nearer than you might think. So if you like living on the edge, feel free to go for it now!</p><p>But here is what is missing for me:</p><ul><li>easy code coverage with Playwright</li><li>Vitest browser mode is still experimental and docs are lacking—getting it to run requires trial and error (although when it works, it works very well)</li><li>the Vitest plugin for Storybook is also experimental, and it will probably be for as long as Vitest browser mode remains so</li></ul><p>That being said, you can start preparing for it now by upgrading your tools. If you’re in a Single Page Application (SPA), make sure to use modern tooling such as <a href="https://vite.dev/">Vite</a> or <a href="https://rsbuild.dev/">Rsbuild</a>. Modern testing requires modern tooling. Depending on how old your project is and the amount of dependencies, this might take some time.</p><p>Then migrate your tests to Vitest. Again, you might encounter some issues with dependencies, but things are improving thanks to authors upgrading their libraries to ESM. This will not dramatically improve the speed of your tests (although you might see marginal gains when updating JSDOM or replacing it with Happy DOM). But since Vitest is a requirement for most tools I presented above, you will be ready to adopt them.</p><p>I would also start adding Storybook to projects with many components and ensure that stories are created when adding a new component. Even if the stories don’t run in your tests yet, the DX will start improving as a result.</p><p>Finally, I highly recommend getting familiar with <a href="https://testing-library.com/">Testing Library</a> and its <a href="https://testing-library.com/docs/guiding-principles">guiding principles</a>. Modern testing tools were built with those principles in mind. Also, if you’re not using <a href="https://mswjs.io/">Mock Service Worker (MSW)</a> yet, you probably should. No matter how and where you test, one constant is that you will have to mock the network. And there’s no better tool for this than MSW.</p><p><em>Hey! I’m Guillaume, a longtime frontend engineer. I want you to know that I will be writing a follow-up article with the actual, concrete, and step-by-step setup for my ideal testing setup. So if you’re looking forward to it, hit ‘</em><strong><em>Follow</em></strong><em>’ and stay tuned!</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=3a52f03ee03c" width="1" height="1" alt=""><hr><p><a href="https://levelup.gitconnected.com/are-playwright-and-vitest-ready-to-replace-jest-3a52f03ee03c">Are Playwright and Vitest ready to replace Jest?</a> was originally published in <a href="https://levelup.gitconnected.com">Level Up Coding</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Module Federation users now have a clear upgrade path!]]></title>
            <link>https://blog.cubed.run/module-federation-users-now-have-a-clear-upgrade-path-1701de23f58e?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/1701de23f58e</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[micro-frontends]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[module-federation]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Wed, 28 Aug 2024 17:31:29 GMT</pubDate>
            <atom:updated>2024-09-11T13:24:45.178Z</atom:updated>
            <content:encoded><![CDATA[<h4>Webpack’s successor has landed, with first-class support for Module Federation.</h4><figure><img alt="The Rspack logo (a crab) lovingly holding the Module Federation logo (a cube composed of smaller cubes)" src="https://cdn-images-1.medium.com/max/1024/1*BY7-2aTEF1FdJMfkC3me0g.png" /><figcaption>Rspack ❤️ Module Federation</figcaption></figure><p>If you’re using <a href="https://webpack.js.org/concepts/module-federation/">Module Federation</a> at work, you’re probably bundling your app with <a href="https://webpack.js.org/">webpack</a>. You might even have used <a href="https://create-react-app.dev/">Create-React-App</a> (CRA) in some projects because you didn’t want to spend hours configuring them (I did 🤫).</p><p>You may have tried migrating to <a href="https://vitejs.dev/">Vite</a> despite the lack of official support for Module Federation. If so, I hope it went well (it can be challenging to convert a webpack project to Vite)…</p><p>Well, rejoice, because there is a new contender in the game: Rspack!</p><h3>Rspack</h3><p><a href="https://rspack.dev/">Rspack</a> aims to succeed at webpack (hopefully, at some point, webpack 6 will officially become Rspack). It’s written in Rust and it’s super fast. But for me, this is where Rspack really shines compared with the competition:</p><ul><li>Rspack supports most of webpack’s configuration, as well as existing plugins and loaders.</li><li>Rspack has first-class official support for Module Federation</li></ul><p>You can read more at:</p><p><a href="https://rspack.dev/">Rspack</a></p><h3>Rsbuild</h3><p>Rspack is at the core of an ecosystem, where you’ll also find <a href="https://rsbuild.dev/">Rsbuild</a>. Rsbuild is to Rspack what Create-React-App (CRA) is to webpack. Well, technically, Rsbuild is framework-agnostic. It supports React, Vue, Svelte, Solid, and more… So the equivalent of Create-React-App would be Rsbuild + its React plugin.</p><p>But just like Create-React-App, you can create a new project from scratch using the CLI (eg: npm create rsbuild@latest). And the upgrade path from CRA is pretty straightforward, <a href="https://rsbuild.dev/guide/migration/cra">thanks to their guide</a>.</p><p>Also like CRA, Rsbuild is batteries-included. It is pre-configured to deliver a complete and performant React app, combined with great Developer Experience (DX). You don’t need to write any configuration to use it. Nonetheless, you may still <a href="https://rsbuild.dev/guide/basic/configure-rspack">override the default bundler configuration (Rspack)</a> if you want to.</p><p>However, please note that, unlike Create-React-App, Rsbuild doesn’t include any testing tool. So you’ll have to manually add Jest to it to be on par with CRA (or better, consider upgrading to Vitest).</p><p>More info at:</p><p><a href="https://rsbuild.dev/">Rsbuild</a></p><h3>Module Federation</h3><p>Now, the part you’ve all been waiting for: Module Federation! Both Rspack and Rsbuild support the <a href="https://module-federation.io/blog/announcement.html#announcement-of-release">brand new version of Module Federation (v2)</a>, which is no longer coupled to webpack (hello Vite!) and supports type sharing when running locally.</p><p><em>But</em>, unless you’re starting a new project from scratch, you will want to stick with the first version of Module Federation (v1.5). Indeed, federated modules on v2 don’t work with modules on v1 (and vice versa).</p><p>This is perfectly fine with me! Since Module Federation v1.5 is compatible with v1, you can gradually update your Micro Frontend (MFE) applications to Rspack (or Rsbuild), and expect them to work seamlessly.</p><p>You’ll get full interoperability regardless of whether your applications use webpack, Rspack, Rsbuild, or a mix of them (I haven’t tried with a Vite application, but I expect you’d get the same results as when using it along with webpack, which I already had some success doing). Also, it doesn’t matter whether you update the host or the remote MFE first, Module Federation just works.</p><p>You can see some examples at:</p><ul><li><a href="https://github.com/rspack-contrib/rspack-examples/tree/main/rspack/module-federation-v1.5">Rspack with Module Federation</a> (v1.5)</li><li><a href="https://github.com/rspack-contrib/rspack-examples/tree/main/rsbuild/module-federation">Rsbuild with Module Federation</a> (v1.5)</li><li><a href="https://github.com/rspack-contrib/rspack-examples/tree/main/rspack/module-federation-interop">Rspack with Module Federation interoperability</a> (Rspack with v1.5 + Rspack with v1 + webpack with v.1)</li></ul><p>Currently, there are no examples available that show Rsbuild running along with other applications, but you can easily verify it works by modifying the last example (which I did).</p><h3>Hot Module Refresh</h3><p>Your experience with Hot Module Refresh (HMR) may vary. It’s always been a mixed bag for me when working with Module Federation, webpack, and/or Create-React-App. Sometimes it works, sometimes it doesn’t.</p><p>However, after upgrading the application to Rspack or Rsbuild, Hot Module Refresh started working, even when mixing it with older webpack MFEs 😅. I can’t promise it’ll work for you as I could not pinpoint what was causing the issue in the first place.</p><p>I’m glad Rspack and Rsbuild nailed that one because you can’t have good developer experience without HMR. So, no more hitting F5 to refresh the page and see your changes! Changes are instant!</p><h3>Upgrade path</h3><p>If you’re on webpack, it makes sense to <a href="https://rspack.dev/guide/migration/webpack">migrate to Rspack</a>. But if you’re on Create-React-App, you should rather <a href="https://rsbuild.dev/guide/migration/cra">migrate to Rsbuild</a>.</p><p>If you’re upgrading to Rspack, you will need to <a href="https://rspack.dev/guide/features/module-federation#module-federation-v15">update your Module Federation configuration</a>. But fear not, it’s almost identical to the one you’re using with webpack. And <a href="https://rsbuild.dev/guide/advanced/module-federation#module-federation-v15">the same goes for Rsbuild</a>.</p><p>However, if you’re using a promise to resolve your remotes, beware that there is currently no official plugin to support it. You’ll have to do the dirty job by hand. See <a href="https://github.com/web-infra-dev/rspack/issues/6077">[Feature]: Module Federation supports runtime URLs</a>.</p><h3>Conclusion</h3><p>I have been playing with Rspack and Rsbuild since the announcement of the alpha version, and my proofs of concept have been successful.</p><p>I have also been impressed by the support I received from the Rspack team; they were fast to answer questions and fix the issue I reported (<a href="https://github.com/web-infra-dev/rsbuild/discussions/2802">HMR in Module Federation</a>)!</p><p>Now that Rspack and Rsbuild are production-ready, I’m excited to start upgrading my projects and enjoy the new developer experience! What about you?</p><h3>Cubed</h3><p><em>Thank you for being a part of the community! Before you go:</em></p><ul><li>Be sure to <strong>clap</strong> and <strong>follow</strong> the writer ️👏<strong>️️</strong></li><li>Follow us: <a href="https://twitter.com/inPlainEngHQ"><strong>X</strong></a> | <a href="https://www.linkedin.com/company/inplainenglish/"><strong>LinkedIn</strong></a> | <a href="https://www.youtube.com/channel/UCtipWUghju290NWcn8jhyAw"><strong>YouTube</strong></a> | <a href="https://discord.gg/in-plain-english-709094664682340443"><strong>Discord</strong></a> | <a href="https://newsletter.plainenglish.io/"><strong>Newsletter</strong></a></li><li>Visit our platforms: <a href="https://cofeed.app/"><strong>CoFeed</strong></a> | <a href="https://differ.blog/"><strong>Differ</strong></a> | <a href="https://plainenglish.io/"><strong>In Plain English</strong></a> | <a href="https://venturemagazine.net/"><strong>Venture</strong></a> | <a href="https://cubed.run/"><strong>Cubed</strong></a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1701de23f58e" width="1" height="1" alt=""><hr><p><a href="https://blog.cubed.run/module-federation-users-now-have-a-clear-upgrade-path-1701de23f58e">Module Federation users now have a clear upgrade path!</a> was originally published in <a href="https://blog.cubed.run">Cubed</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Why I won’t put my stories behind a paywall]]></title>
            <description><![CDATA[<div class="medium-feed-item"><p class="medium-feed-image"><a href="https://medium.com/@gfox1984/why-i-wont-put-my-stories-behind-a-paywall-664c7eae8872?source=rss-c0c83930ac5f------2"><img src="https://cdn-images-1.medium.com/max/2600/0*CVK0tb3cp7VWrcLt" width="5149"></a></p><p class="medium-feed-snippet">Except for this one. Let me explain.</p><p class="medium-feed-link"><a href="https://medium.com/@gfox1984/why-i-wont-put-my-stories-behind-a-paywall-664c7eae8872?source=rss-c0c83930ac5f------2">Continue reading on Medium »</a></p></div>]]></description>
            <link>https://medium.com/@gfox1984/why-i-wont-put-my-stories-behind-a-paywall-664c7eae8872?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/664c7eae8872</guid>
            <category><![CDATA[paywall]]></category>
            <category><![CDATA[writing]]></category>
            <category><![CDATA[medium]]></category>
            <category><![CDATA[learning]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Wed, 14 Aug 2024 18:01:35 GMT</pubDate>
            <atom:updated>2024-08-20T07:35:24.543Z</atom:updated>
        </item>
        <item>
            <title><![CDATA[How to implement Tabs that sync with React Router]]></title>
            <link>https://blog.stackademic.com/how-to-implement-tabs-that-sync-with-react-router-e255e0e90cfd?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/e255e0e90cfd</guid>
            <category><![CDATA[react-aria]]></category>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[react-router]]></category>
            <category><![CDATA[javascript]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Wed, 07 Aug 2024 06:01:50 GMT</pubDate>
            <atom:updated>2024-08-21T14:48:38.885Z</atom:updated>
            <content:encoded><![CDATA[<h4>Syncing tabs with the current location can be tricky, especially when dealing with relative paths.</h4><figure><img alt="The React Router logo mark, with a tab list component under, showing three tabs: Tab 1, Tab 2, Tab 3." src="https://cdn-images-1.medium.com/max/1024/1*kueb1P_oava3iUFzjZJMdw.png" /><figcaption>The React Router name and logos are trademarks of Remix Software Inc.</figcaption></figure><p>I remember the first time I tried to use my own Tab component with <a href="https://reactrouter.com/">React Router</a>. I thought it would be relatively easy to sync tabs with the current location. At worst, I should be able to find some examples online…</p><p>I was wrong.</p><p>Most tutorials show you how to match routes based on an absolute path, which works great… provided you know the full path and are willing to hardcode it. This was not an option for me.</p><p>I wanted my tabs to match paths <strong>within</strong> the parent route, no matter what path the parent route was using. If the path changed, or the tabs were to be nested under another route, the tabs should still work.</p><h3>Scenario</h3><p>We want to implement this app (the designer is on holiday and no one cares about the UI in the company 🙈):</p><figure><img alt="Screenshot of a very lame page showing 3 tabs. The tab matching the URL is selected. The content of the selected tab is displayed below." src="https://cdn-images-1.medium.com/max/618/1*8pqTGzdlrZR8gLfggAv1yA.png" /><figcaption>Lame page with 3 tabs that update according to the URL</figcaption></figure><p>The app consists of a single page with 3 tabs. Whenever the tab number changes in the URL, the corresponding tab should be selected, and its content should be shown.</p><p>When we click a tab, the URL updates accordingly.</p><p>Right now, the page displays at /sub. But this is likely to change in the future, so the tabs should work whether the page is rendered here or at /any/other/path.</p><p>We get extra brownie points for implementing <a href="https://daily-dev-tips.com/posts/a11y-101-tabs/">accessible tabs</a>.</p><h3>Solution 1: &lt;NavLink&gt;</h3><p>The easiest solution is to use the <a href="https://reactrouter.com/en/main/components/nav-link">&lt;NavLink&gt;</a> component from React Router:</p><pre>import { NavLink } from &quot;react-router-dom&quot;;<br><br>// ...<br><br>&lt;NavLink to=&quot;1&quot;&gt;Tab 1&lt;/NavLink&gt;<br>&lt;NavLink to=&quot;2&quot;&gt;Tab 2&lt;/NavLink&gt;<br>&lt;NavLink to=&quot;3&quot;&gt;Tab 3&lt;/NavLink&gt;</pre><p>&lt;NavLink&gt; automatically applies the active class name when the path in the to prop matches the current location. This works for both absolute and relative paths (relative paths resolve relative to the parent route).</p><p>It also has a few options to customize the active class name, the style, or children, and to tweak the default behavior. See <a href="https://reactrouter.com/en/main/components/nav-link">the docs</a> for details and <a href="https://reactrouter.com/en/main/start/tutorial#active-link-styling">the official tutorial</a> for a complete example.</p><p>The interactive sandbox below uses the &lt;NavLink&gt; component to implement the scenario:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodesandbox.io%2Fembed%2Fqfztfd%3Fview%3Deditor%2B%252B%2Bpreview%26module%3D%252Fsrc%252FApp.tsx&amp;display_name=CodeSandbox&amp;url=https%3A%2F%2Fcodesandbox.io%2Fs%2Fqfztfd&amp;image=https%3A%2F%2Fcodesandbox.io%2Fapi%2Fv1%2Fsandboxes%2Fqfztfd%2Fscreenshot.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codesandbox" width="1000" height="500" frameborder="0" scrolling="no"><a href="https://medium.com/media/7b9b7f099d8ab75c841d0cc3572a31ed/href">https://medium.com/media/7b9b7f099d8ab75c841d0cc3572a31ed/href</a></iframe><p>All is well, but things are never that easy (or are they?):</p><ul><li>&lt;NavLink&gt; renders a hyperlink (&lt;a&gt; tag); <em>maybe</em> you don’t want that?</li><li>&lt;NavLink&gt; uses the aria-current attribute for accessibility; <em>maybe</em> you’d rather use the aria-selected attribute along with the tab role?</li><li><em>maybe</em> you want to use another library to render your tabs?</li><li><em>maybe</em> you have your own reasons?</li></ul><p>Enough <em>maybes</em>, let’s explore other solutions now.</p><h3>Solution 2: Router-aware Tabs</h3><p>OK, so we’ll start with custom Tabs. Since we’re good people (and we like brownies), we’ll also try to make them accessible.</p><p>Let’s implement a component that plugs directly into the router, just like &lt;NavLink&gt;. We can use the following code (inspired by &lt;NavLink&gt;’s <a href="https://github.com/remix-run/react-router/blob/55c18bfc8e20417204cce5ecda88fe03520ad257/packages/react-router-dom/index.tsx#L1057-L1171">source code</a>):</p><pre>import { ReactNode } from &quot;react&quot;;<br>import {<br>  useLocation,<br>  useNavigate,<br>  useResolvedPath,<br>  To,<br>} from &quot;react-router-dom&quot;;<br><br>interface RouterTabProps {<br>  children: ReactNode;<br>  to: To;<br>}<br><br>function RouterTab({ children, to }: RouterTabProps) {<br>  const navigate = useNavigate();<br><br>  const { pathname: toPathname } = useResolvedPath(to);<br>  const { pathname: locationPathname } = useLocation();<br><br>  const selected = locationPathname.startsWith(toPathname);<br><br>  return (<br>    &lt;button role=&quot;tab&quot; onClick={() =&gt; navigate(to)} aria-selected={selected}&gt;<br>      {children}<br>    &lt;/button&gt;<br>  );<br>}</pre><p>We have a &lt;RouterTab&gt; component that receives a path via the to prop (same as &lt;NavLink&gt;). This path can be either relative or absolute.</p><p>The path is resolved (useResolvedPath(to)) and matched against the current location (locationPathname.startsWith(toPathname)). If it matches, we set the aria-selected attribute to true, which provides basic accessibility and allows for styling.</p><p>Note that for the sake of this exercise, the component is implemented as a &lt;button&gt; with the tab role, which navigates when clicked.</p><p>We can now replace the &lt;NavLink&gt; components in solution 1 with &lt;RouterTab&gt;s:</p><pre>&lt;div role=&quot;tablist&quot;&gt;<br>  &lt;RouterTab to=&quot;1&quot;&gt;Tab 1&lt;/RouterTab&gt;<br>  &lt;RouterTab to=&quot;2&quot;&gt;Tab 2&lt;/RouterTab&gt;<br>  &lt;RouterTab to=&quot;3&quot;&gt;Tab 3&lt;/RouterTab&gt;<br>&lt;/div&gt;</pre><p>The full example is available in this sandbox:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodesandbox.io%2Fembed%2F73x7rr%3Fview%3Deditor%2B%252B%2Bpreview%26module%3D%252Fsrc%252FApp.tsx&amp;display_name=CodeSandbox&amp;url=https%3A%2F%2Fcodesandbox.io%2Fs%2F73x7rr&amp;image=https%3A%2F%2Fcodesandbox.io%2Fapi%2Fv1%2Fsandboxes%2F73x7rr%2Fscreenshot.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codesandbox" width="1000" height="500" frameborder="0" scrolling="no"><a href="https://medium.com/media/b5e0b1eba331924f15e2431a78ec20b8/href">https://medium.com/media/b5e0b1eba331924f15e2431a78ec20b8/href</a></iframe><p>Although this approach works, we should consider using tabs that aren’t tightly coupled with the router. This way, our tabs can be reused anywhere independently from React Router.</p><p>Let’s explore another solution.</p><h3>Solution 3: Generic Tabs</h3><p>A generic tab component that implements basic accessibility might look like this:</p><pre>import { ReactNode } from &quot;react&quot;;<br><br>interface TabListProps {<br>  children: ReactNode;<br>}<br><br>interface TabProps {<br>  children: ReactNode;<br>  selected?: boolean;<br>  onClick?: () =&gt; void;<br>}<br><br>function TabList({ children }: TabListProps) {<br>  return &lt;div role=&quot;tablist&quot;&gt;{children}&lt;/div&gt;;<br>}<br><br>function Tab({ children, selected, onClick }: TabProps) {<br>  return (<br>    &lt;button role=&quot;tab&quot; onClick={onClick} aria-selected={selected}&gt;<br>      {children}<br>    &lt;/button&gt;<br>  );<br>}</pre><p>Here we have a dumb &lt;TabList&gt; container and the actual &lt;Tab&gt; (a button with selected and onClick props). Your implementation might be smarter, but it doesn’t matter. The point is that the tabs don’t know anything about React Router or the current location.</p><p>To determine which tab is selected, you usually see solutions such as:</p><pre>import { useLocation, useNavigate } from &quot;react-router-dom&quot;;<br><br>// ...<br><br>const navigate = useNavigate();<br>const { pathname } = useLocation();<br><br>// ...<br><br>&lt;TabList&gt;<br>  &lt;Tab onClick={() =&gt; navigate(&quot;1&quot;)} selected={pathname === &quot;/sub/1&quot;}&gt;<br>    Tab 1<br>  &lt;/Tab&gt;<br>  &lt;Tab onClick={() =&gt; navigate(&quot;2&quot;)} selected={pathname === &quot;/sub/2&quot;}&gt;<br>    Tab 2<br>  &lt;/Tab&gt;<br>  &lt;Tab onClick={() =&gt; navigate(&quot;3&quot;)} selected={pathname === &quot;/sub/3&quot;}&gt;<br>    Tab 3<br>  &lt;/Tab&gt;<br>&lt;/TabList&gt;</pre><p>This works well… provided you’re fine hardcoding the full path in the component (&quot;/sub/1&quot;). We’re not. See how we call navigate(&quot;1&quot;) to switch to <strong>Tab 1</strong>? We want the same for selecting it. So we should use a relative path instead. Something like this:</p><pre>- &lt;Tab onClick={() =&gt; navigate(&quot;1&quot;)} selected={pathname === &quot;/sub/1&quot;}&gt;<br>+ &lt;Tab onClick={() =&gt; navigate(&quot;1&quot;)} selected={pathname === &quot;1&quot;}&gt;</pre><p>OK, so how do you determine that a relative path matches the current location when your component doesn’t even know under which route it is rendered?</p><p>Here is one solution:</p><pre>import { useMatch, useNavigate, useResolvedPath } from &quot;react-router-dom&quot;;<br><br>// ...<br><br>const navigate = useNavigate();<br><br>const { pathname } = useResolvedPath(&quot;&quot;);<br>const route = useMatch(`${pathname}/:path/*`);<br>const matchedPath = route?.params.path;<br><br>// ...<br><br>&lt;Tab onClick={() =&gt; navigate(&quot;1&quot;)} selected={matchedPath === &quot;1&quot;}&gt;</pre><p>Let me explain:</p><ol><li>const { pathname } = useResolvedPath(&quot;&quot;) gives you the resolved path for the current route. Why? Because useResolvedPath resolves &quot;&quot; relative to the parent route. So this gives you the full path of the route where the component is rendered (&quot;.&quot; would work too).</li><li>const route = useMatch(`${pathname}/:path/*`) will match anything under the current route (which we resolved to pathname). :path is used to extract the first segment of the path under that route. In other words, the relative path we’re after.</li><li>const matchedPath = route?.params.path extracts the :path value that we’ve just matched.</li></ol><p>This feels harder than it needs to be. It’s not very elegant either, but this is the best I could find (this code has been working effectively for some time now). If you can think of a better way, please reach out 🙏</p><p>Let’s update our tabs with the code:</p><pre>import { useMatch, useNavigate, useResolvedPath } from &quot;react-router-dom&quot;;<br><br>// ...<br><br>const navigate = useNavigate();<br><br>const { pathname } = useResolvedPath(&quot;&quot;);<br>const route = useMatch(`${pathname}/:path/*`);<br>const matchedPath = route?.params.path;<br><br>// ...<br><br>&lt;TabList&gt;<br>  &lt;Tab onClick={() =&gt; navigate(&quot;1&quot;)} selected={matchedPath === &quot;1&quot;}&gt;<br>    Tab 1<br>  &lt;/Tab&gt;<br>  &lt;Tab onClick={() =&gt; navigate(&quot;2&quot;)} selected={matchedPath === &quot;2&quot;}&gt;<br>    Tab 2<br>  &lt;/Tab&gt;<br>  &lt;Tab onClick={() =&gt; navigate(&quot;3&quot;)} selected={matchedPath === &quot;3&quot;}&gt;<br>    Tab 3<br>  &lt;/Tab&gt;<br>&lt;/TabList&gt;</pre><p>Now you have a generic Tab component that you can use with relative paths. See the full implementation for yourself:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodesandbox.io%2Fembed%2Ft2pcjk%3Fview%3Deditor%2B%252B%2Bpreview%26module%3D%252Fsrc%252FApp.tsx&amp;display_name=CodeSandbox&amp;url=https%3A%2F%2Fcodesandbox.io%2Fs%2Ft2pcjk&amp;image=https%3A%2F%2Fcodesandbox.io%2Fapi%2Fv1%2Fsandboxes%2Ft2pcjk%2Fscreenshot.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codesandbox" width="1000" height="500" frameborder="0" scrolling="no"><a href="https://medium.com/media/fe6f38ec3065c0cd09aa6bcd4b2895ea/href">https://medium.com/media/fe6f38ec3065c0cd09aa6bcd4b2895ea/href</a></iframe><p>Custom components are effective for addressing specific problems. They are also a good way to learn. However, it’s not advisable to reinvent the wheel... The custom tabs we’ve developed do not cover every scenario or edge case, and they are not fully accessible either.</p><p>For these reasons, let’s explore one last solution based on a third-party library.</p><h3>Solution 4: Third-Party Tabs (React Aria)</h3><p>Because we love good components and we want all the brownies 🤤, let’s use <a href="https://react-spectrum.adobe.com/react-aria/index.html">React Aria</a> this time (React Aria comes with great accessibility).</p><p>The <a href="https://react-spectrum.adobe.com/react-aria/Tabs.html#links">Tabs example</a> on their website shows how to <a href="https://react-spectrum.adobe.com/react-aria/routing.html">integrate with React Router</a>. Unfortunately, the tutorial relies on absolute paths, which we don’t want.</p><p>So let’s mix our custom solution with React Aria tabs:</p><pre>import { <br>  useHref, <br>  useMatch,<br>  useNavigate,<br>  useResolvedPath,<br>} from &quot;react-router-dom&quot;;<br><br>import {<br>  RouterProvider,<br>  Tabs,<br>  TabList,<br>  Tab,<br>} from &quot;react-aria-components&quot;;<br><br>// ...<br><br>const { pathname } = useResolvedPath(&quot;&quot;);<br>const route = useMatch(`${pathname}/:path/*`);<br>const matchedPath = route?.params.path;<br><br>// ...<br><br>&lt;RouterProvider navigate={useNavigate()} useHref={useHref}&gt;<br>  &lt;Tabs selectedKey={matchedPath}&gt;<br>    &lt;TabList aria-label=&quot;Tabs&quot;&gt;<br>      &lt;Tab id=&quot;1&quot; href=&quot;1&quot;&gt;<br>        Tab 1<br>      &lt;/Tab&gt;<br>      &lt;Tab id=&quot;2&quot; href=&quot;2&quot;&gt;<br>        Tab 2<br>      &lt;/Tab&gt;<br>      &lt;Tab id=&quot;3&quot; href=&quot;3&quot;&gt;<br>        Tab 3<br>      &lt;/Tab&gt;<br>    &lt;/TabList&gt;<br>  &lt;/Tabs&gt;<br>&lt;/RouterProvider&gt;</pre><p>The part that matches the path is unchanged. However, there are a few things worth explaining:</p><ol><li>&lt;RouterProvider navigate={useNavigate()} useHref={useHref}&gt; allows for client-side routing. useNavigate and useHref are used to generate links that work with React Router. This is explained in <a href="https://react-spectrum.adobe.com/react-aria/routing.html#react-router">their docs</a>.</li><li>&lt;Tabs selectedKey={matchedPath}&gt; creates a container for the tabs, with the ID of the selected tab (matchedPath)</li><li>&lt;TabList aria-label=&quot;Tabs&quot;&gt; creates the actual container with the tablist role and an accessible label</li><li>&lt;Tab id=&quot;1&quot; href=&quot;1&quot;&gt; is the actual tab element (tab role). href is the relative path for the tab, and id matches that path, so that the tab gets selected when selectedKey is the same.</li></ol><p>When a tab matches, React Aria applies the aria-selected=&quot;true&quot; attribute.</p><p>For the full example, see:</p><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fcodesandbox.io%2Fembed%2F7gly3n%3Fview%3Deditor%2B%252B%2Bpreview%26module%3D%252Fsrc%252FApp.tsx&amp;display_name=CodeSandbox&amp;url=https%3A%2F%2Fcodesandbox.io%2Fs%2F7gly3n&amp;image=https%3A%2F%2Fcodesandbox.io%2Fapi%2Fv1%2Fsandboxes%2F7gly3n%2Fscreenshot.png&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=codesandbox" width="1000" height="500" frameborder="0" scrolling="no"><a href="https://medium.com/media/754cf1a27cd2e9dd1514f120744d9912/href">https://medium.com/media/754cf1a27cd2e9dd1514f120744d9912/href</a></iframe><h3>Conclusion</h3><p>Syncing tabs with React Router is definitely achievable… once you know how to do it!</p><p>I would advise you to stick to &lt;NavLink&gt; as much as possible, or even use absolute paths if it’s not an issue. But if you need to use relative paths and &lt;NavLink&gt; isn’t an option, now you know what to do...</p><p>The techniques applied here will work with any component library. Moreover, their use is not limited to tabs alone. They can be used to create breadcrumbs, navigation menus, or any element that needs to synchronize with the current location.</p><h3>Stackademic 🎓</h3><p>Thank you for reading until the end. Before you go:</p><ul><li>Please consider <strong>clapping</strong> and <strong>following</strong> the writer! 👏</li><li>Follow us <a href="https://twitter.com/stackademichq"><strong>X</strong></a> | <a href="https://www.linkedin.com/company/stackademic"><strong>LinkedIn</strong></a> | <a href="https://www.youtube.com/c/stackademic"><strong>YouTube</strong></a> | <a href="https://discord.gg/in-plain-english-709094664682340443"><strong>Discord</strong></a></li><li>Visit our other platforms: <a href="https://plainenglish.io/"><strong>In Plain English</strong></a> | <a href="https://cofeed.app/"><strong>CoFeed</strong></a> | <a href="https://differ.blog/"><strong>Differ</strong></a></li><li>More content at <a href="https://stackademic.com/"><strong>Stackademic.com</strong></a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=e255e0e90cfd" width="1" height="1" alt=""><hr><p><a href="https://blog.stackademic.com/how-to-implement-tabs-that-sync-with-react-router-e255e0e90cfd">How to implement Tabs that sync with React Router</a> was originally published in <a href="https://blog.stackademic.com">Stackademic</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing the useStable(value) hook with React Query]]></title>
            <link>https://medium.com/bitsrc/implementing-the-usestable-value-hook-with-react-query-045999cf5c38?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/045999cf5c38</guid>
            <category><![CDATA[react-query]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[react-hook]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Tue, 30 Jul 2024 17:02:27 GMT</pubDate>
            <atom:updated>2024-07-30T17:02:27.346Z</atom:updated>
            <content:encoded><![CDATA[<h4>One thing I love about React Query is that the data it returns is stable. What if we could reuse its code to stabilize our objects?</h4><figure><img alt="// useStable.ts
 
 import { useEffect, useRef } from “react”;
 import { replaceEqualDeep } from “@tanstack/react-query”;
 
 export function useStable&lt;T&gt;(value: T) {
 const ref = useRef(value);
 const stable = replaceEqualDeep(ref.current, value);
 useEffect(() =&gt; {
 ref.current = stable;
 }, [stable]);
 return stable;
 }" src="https://cdn-images-1.medium.com/max/1024/0*KdiJFa3eVQy6xn7S" /><figcaption>useStable(value) hook — Image built with <a href="https://ray.so/">ray.so</a></figcaption></figure><h3>The React Query way</h3><p>As you may know, <a href="https://tanstack.com/query">React Query</a> returns the same instance (or parts of it) when the JSON in the server response remains the same between fetches (or partly the same).</p><p>React Query does this by deeply comparing the data and reusing instances for the parts that don’t change. This feature is called <a href="https://tkdodo.eu/blog/react-query-render-optimizations#structural-sharing">structural sharing</a>:</p><p><a href="https://tkdodo.eu/blog/react-query-render-optimizations#structural-sharing">React Query Render Optimizations</a></p><p>Structural sharing is important because React uses referential identity to decide whether to recompute memos, run effects, or rerender memoized components. If React Query returned new instances each time it fetched (or worse, each time the hook was called), apps would unnecessarily rerender (or worse, get caught in infinite rendering loops).</p><h3>The broken way</h3><p>If you’ve written a React hook before, there is a chance that you’ve also attempted to memoize its return value, so that it stays the same between renders (unless the value is truly different).</p><p>The usual way to achieve this is to useMemo. But this can get hairy when the memo depends on one of the inputs since you have little control over them.</p><p>For example, imagine you have a hook that takes an array in parameters and returns a computed array as output. The implementation might look like this:</p><pre>export function useSomething(array) {<br>  const computed = useMemo(() =&gt; {<br>    return heavyDuty(array);<br>  }, [array]);<br>  return computed;<br>}</pre><p>The output isn’t recomputed unless array changes. But, this only works if the caller is cautious about memoizing the array, which might or might not be the case…</p><p>For example, the hook could be called like this:</p><pre>const filteredArray = array.filter(item =&gt; item.prop === someVar);<br><br>const something = useSomething(filteredArray);</pre><p>Since filteredArray is a new instance each time React renders, the memo in the hook won’t have any effect!</p><p>So if your hook is:</p><ul><li>generic enough,</li><li>performance-critical,</li><li>or used in many places</li></ul><p>You can’t just assume the caller will memoize all the necessary inputs beforehand.</p><p>Also maybe you don&#39;t want to punish the caller for not knowing all about your implementation details… Imagine if React Query forced you to memoize all your keys and inputs! I bet the user adoption wouldn’t be as high as it is now…</p><h3>Stabilizing values with React Query</h3><p>React Query uses the replaceEqualDeep function to achieve stability, or <em>structural sharing</em> (see <a href="https://github.com/TanStack/query/blob/74f10b773f2b1b5f5558d9db3ade0ba1e364fb05/packages/query-core/src/utils.ts#L238-L281">source code</a> and <a href="https://github.com/TanStack/query/blob/74f10b773f2b1b5f5558d9db3ade0ba1e364fb05/packages/query-core/src/__tests__/utils.test.tsx#L140-L393">unit tests</a>).</p><p>As things turn out, we can reuse the same function in our code to implement a hook that ensures a value remains stable between renders.</p><p>Introducing… useStable!</p><pre>import { useEffect, useRef } from &quot;react&quot;;<br>import { replaceEqualDeep } from &quot;@tanstack/react-query&quot;;<br><br>export function useStable&lt;T&gt;(value: T) {<br>  const ref = useRef(value);<br>  const stable = replaceEqualDeep(ref.current, value);<br>  useEffect(() =&gt; {<br>    ref.current = stable;<br>  }, [stable]);<br>  return stable;<br>}</pre><p>What the code does:</p><ol><li>Export a function called useStable that takes a value as input</li><li>Use a ref to store the stable value (initially set to value)</li><li>Pass the current value and the previous one (stored in the ref) to replaceEqualDeep, which returns a stable value.</li><li>Update the ref with the stablevalue whenever it changes</li><li>Return the stable value</li></ol><p>Step 3 is where the magic happens. React Query’s replaceEqualDeep function returns an instance that preserves identity at all levels. This means that:</p><ul><li>if the object or array is the same, it returns the same object or array</li><li>but also, if not strictly the same, parts that remain the same (object properties or items in an array) still point to the same instances</li></ul><h4>Stabilizing the inputs</h4><p>Now that we have a way to stabilize a value, let’s update our hook and stop worrying about ever-changing inputs:</p><pre>export function useSomething(array) {<br>  const stableArray = useStable(array);<br>  const computed = useMemo(() =&gt; {<br>    return heavyDuty(stableArray);<br>  }, [stableArray]);<br>  return computed;<br>}</pre><p>Thanks to useStable, the memo will work even if the array is a new instance at each render, provided the items inside the array remain the same.</p><h4>Stabilizing the output</h4><p>In the previous example, we stabilized the inputs. We did it because the computation was costly, and we wanted to do it as few times as possible. But in most cases, computing a value is cheap.</p><p>So instead of stabilizing the input, we could stabilize the output and take advantage of structural sharing.</p><p>For example, consider this code:</p><pre>export function useFiltered(array) {<br>  const computed = useMemo(() =&gt; {<br>    return cheapOperation(array);<br>  }, [array]);<br>  return computed;<br>}</pre><p>We can get rid of the memo and guarantee the stability of the output by changing the function to:</p><pre>export function useFiltered(array) {<br>  const computed = cheapOperation(array);<br>  const stable = useStable(computed);<br>  return stable;<br>}</pre><p>The tradeoff is that we now re-compute the result even if the array hasn’t changed. But since the operation is cheap, it doesn’t matter.</p><p>This code however has one significant advantage. When the computed value partially changes between renders, the identity of the values that remain the same will be preserved (the same as with React Query)!</p><h3>Wrapping up</h3><p>Well, that was fun!</p><p>Now, for the big question: should I use it?</p><p><em>It depends.</em></p><p>I found myself using this hook on rare occasions where returning a stable value was important, but I couldn’t simply use a memo because the input wasn’t guaranteed to be stable (eg: an array recreated at each render).</p><p>Also, there may be a few edge cases, such as dealing with a variable number of dependencies, where you can’t useMemo (eg: calling <a href="https://tanstack.com/query/v4/docs/framework/react/reference/useQueries">useQueries</a> and returning a stable array with the data for each query). useStable doesn’t have such limitations.</p><p>Having said that, memos should get the job done most of the time. Also useMemo uses <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/is">Object.is</a> to compare dependencies and decide whether to run the code, which is cheaper than calling replaceEqualDeep.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=045999cf5c38" width="1" height="1" alt=""><hr><p><a href="https://medium.com/bitsrc/implementing-the-usestable-value-hook-with-react-query-045999cf5c38">Implementing the useStable(value) hook with React Query</a> was originally published in <a href="https://medium.com/bitsrc">Bits and Pieces</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fully typed Web Apps with OpenAPI (Part 2)]]></title>
            <link>https://blog.stackademic.com/fully-typed-web-apps-with-openapi-part-2-6ae1b8c6de92?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/6ae1b8c6de92</guid>
            <category><![CDATA[programming]]></category>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[open-api]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[react]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Mon, 20 May 2024 06:02:19 GMT</pubDate>
            <atom:updated>2024-08-09T00:40:46.805Z</atom:updated>
            <content:encoded><![CDATA[<h4>In this episode, OpenAPI generator takes your web app to new heights.</h4><figure><img alt="A superhero flying the sky, with a light green cape, a light green mask on his eyes, light grey clothes, red gloves, red belt, red boots, dark hair, who is a developer and not very muscular." src="https://cdn-images-1.medium.com/max/1024/0*HUHXpSGP2Pg-hFN1" /><figcaption>Image generated with AI (Microsoft Copilot)</figcaption></figure><h3>Introduction</h3><p>In <a href="https://medium.com/@gfox1984/fully-typed-web-apps-with-openapi-part-1-595d55766670">Part 1</a>, we introduced OpenAPI generator and we saw the benefits of using it to generate a fully typed client API.</p><p>However, generating a client API is rarely a one-off operation: when you’re working on a new project, the APIs you are consuming are likely to change or receive patches regularly.</p><p>So this time, I’ll show you how to build API generation directly into your project. Here, we’ll create a new web application from scratch using a modern stack, but feel free to plug it into your own stack.</p><p>Here is the program:</p><ol><li><a href="#3626">Create a web app with Vite, React, and TypeScript</a></li><li><a href="#9ea5">Generate the API with OpenAPI Generator</a></li><li><a href="#4557">Create custom hooks with React Query</a></li><li><a href="#4d35">Consume the API</a></li><li><a href="#8f5e">Mock the API with MSW</a></li><li><a href="#9da4">Style with Tailwind CSS (optional)</a></li><li><a href="#eb53">Test with Vitest and Testing Library</a></li></ol><h3>1. Create a web app with Vite, React, and TypeScript</h3><p>OK so, first things first: let’s create a web application (or reuse yours). To make things easier, I’ve decided to stick with a simple Single Page Application (SPA) instead of using a meta-framework like Next.js or Remix.</p><p>So for this project, we will use <a href="https://vitejs.dev/">Vite</a> with React and TypeScript. And since we’re on a modern stack, opting for <a href="https://pnpm.io/">PNPM</a> over NPM makes sense too:</p><pre>pnpm create vite petstore-app --template react-ts</pre><p>Done!</p><p>🔎 <em>The source code for step 1 is available at: </em><a href="https://github.com/gfox1984/petstore-app/commit/5b8c6c1"><em>https://github.com/gfox1984/petstore-app/commit/5b8c6c1</em></a></p><h3>2. Generate the API with OpenAPI Generator</h3><h4>2.1. Setup</h4><p>For this exercise, we will continue using the Pet Store API from <a href="https://medium.com/@gfox1984/fully-typed-web-apps-with-openapi-part-1-595d55766670">Part 1</a>. So go ahead and copy <a href="https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml">the YAML file</a> into src/api/petstore/api.yaml. It will be the source for generating our client API.</p><p>Next, add the OpenAPI Generator to the project with:</p><pre>pnpm add -D @openapitools/openapi-generator-cli</pre><p>Then, add a new script to package.json to invoke it:</p><pre>&quot;scripts&quot;: {<br>  ...  <br>  &quot;generate-api:petstore&quot;: &quot;openapi-generator-cli generate -i ./src/api/petstore/api.yaml -g typescript-axios -o ./src/api/petstore/ --skip-validate-spec --additional-properties=useSingleRequestParameter=true,withoutPrefixEnums=true,enumNameSuffix=,withSeparateModelsAndApi=true,apiPackage=server,modelPackage=model --openapi-normalizer REFACTOR_ALLOF_WITH_PROPERTIES_ONLY=true&quot;<br>}</pre><p>You must be thinking, “Wow, that’s a heck of a script!”</p><p>Indeed! The exact script was written after a lot of trial and error, but it has served me well so far. Let me explain:</p><ul><li>generate-api:petstore is the name of the script used to generate the petstore API. If you want to add another script later on and generate another API (pretty common), follow the same pattern.</li><li>openapi-generator-cli generate invokes the OpenAPI Generator CLI with the <a href="https://openapi-generator.tech/docs/usage/#generate">generate</a> command</li><li>-i ./src/api/petstore/api.yaml is the OpenAPI file to read from</li><li>-g typescript-axios indicates that we want to use the <a href="https://openapi-generator.tech/docs/generators/typescript-axios">typescript-axios</a> generator</li><li>-o ./src/api/petstore/ is the output directory for the generated code</li><li>--aditional-properties specifies additional properties that are used to tweak the output: <br>useSingleRequestParameter=true generates functions that take a single object in arguments, wrapping all the request parameters. This avoids issues when the number or the order of parameters changes over time.<br>withoutPrefixEnums=true and enumNameSuffix= are used to preserve the enum names that are defined in the OpenAPI file<br>withSeparateModelsAndApi, used along with apiPackage=server and modelPackage=model, improves readability by splitting the generated API and models into distinct folders</li><li>--openapi-normalizer REFACTOR_ALLOF_WITH_PROPERTIES_ONLY=true is used to prevent <a href="https://stackoverflow.com/questions/73853636/open-api-generator-allof-not-generating-model-correctly">issues when using allOf with multiple objects</a></li></ul><p>I hope it makes more sense now. Based on your needs and your API, you might want to adjust those settings. See the documentation for <a href="https://openapi-generator.tech/docs/generators/typescript-axios">typescript-axios</a>.</p><p>Before we wrap this section, let’s not forget to add axios to our list of dependencies (the generated code uses <a href="https://axios-http.com/docs/intro">Axios</a>):</p><pre>pnpm add axios</pre><h4>2.2. Generate</h4><p>Now that we are set up, we can generate the API using:</p><pre>pnpm run generate-api:petstore</pre><p>After the script completes, a bunch of files are generated inside the src/api/petstore folder (see <a href="https://medium.com/@gfox1984/fully-typed-web-apps-with-openapi-part-1-595d55766670">Part 1</a> for details).</p><p>Some of these files aren’t useful, so we can delete them and add them to src/api/petstore/.openapi-generator-ignore:</p><pre>.gitignore<br>git_push.sh<br>.npmignore</pre><p>We can also add the .openapi-generator folder to our .gitignore :</p><pre># api<br>.openapi-generator</pre><h4>2.3. Export</h4><p>Finally, I also recommend adding an index.ts file at the root of the src/api folder, where we instantiate and export each API, with a default configuration:</p><pre>import { Configuration, PetApi, StoreApi, UserApi } from &quot;./petstore&quot;;<br><br>const configuration = new Configuration();<br><br>export const petApi = new PetApi(configuration);<br>export const storeApi = new StoreApi(configuration);<br>export const userApi = new UserApi(configuration);</pre><p>This can be useful in projects where we want to specify a different base URL or use a particular <a href="https://axios-http.com/docs/intro">Axios</a> instance.</p><p>🔎 <em>The source code for step 2 is available at: </em><a href="https://github.com/gfox1984/petstore-app/commit/13bbaa9"><em>https://github.com/gfox1984/petstore-app/commit/13bbaa9</em></a></p><h3>3. Create custom hooks with React Query</h3><p>Before we start using this brand-new API, we need to determine our fetching strategy. We certainly <a href="https://react.dev/reference/react/useEffect#what-are-good-alternatives-to-data-fetching-in-effects">don’t want to fetch in effects</a>. Since we’re in a SPA, I recommend using <a href="https://tanstack.com/query/latest/docs/framework/react/overview">React Query</a>, with <a href="https://tkdodo.eu/blog/practical-react-query#create-custom-hooks">custom hooks</a>.</p><p>So let’s install React Query:</p><pre>pnpm add @tanstack/react-query</pre><p>Then, configure the app at src/App.tsx with a QueryClient:</p><pre>import { QueryClient, QueryClientProvider } from &quot;@tanstack/react-query&quot;;<br><br>const queryClient = new QueryClient();<br><br>function App() {<br>  return (<br>    &lt;QueryClientProvider client={queryClient}&gt;TODO&lt;/QueryClientProvider&gt;<br>  );<br>}<br><br>export default App;</pre><p>Now let’s add a custom usePets hook that fetches the list of pets using the findPetsByStatus method of the PetApi.</p><p>To do this, create a src/hooks/pet-hooks.ts file with:</p><pre>import { useQuery } from &quot;@tanstack/react-query&quot;;<br>import { petApi } from &quot;../api&quot;;<br>import { FindPetsByStatusStatus } from &quot;../api/petstore&quot;;<br><br>const allStatuses: FindPetsByStatusStatus[] = [&quot;available&quot;, &quot;pending&quot;, &quot;sold&quot;];<br><br>export function usePets(status = allStatuses) {<br>  return useQuery({<br>    queryKey: [&quot;pet&quot;, &quot;findPetsByStatus&quot;, status],<br>    queryFn: async () =&gt; (await petApi.findPetsByStatus({ status })).data,<br>  });<br>}</pre><p>The usePets hook takes optional filters in parameters, which it then forwards to the Pet API via the query function (queryFn).</p><p>useQuery does all the dirty work for us: updating the loading state, handling errors, caching, etc. And it automatically uses the QueryClient that we configured in the App above. Just make sure you have a unique queryKey for each API and parameters you use in queryFn. The <a href="https://tanstack.com/query/latest/docs/eslint/eslint-plugin-query">ESLint Query Plugin</a> can help you with that.</p><p>🔎 <em>The source code for step 3 is available at: </em><a href="https://github.com/gfox1984/petstore-app/commit/e65b4ba"><em>https://github.com/gfox1984/petstore-app/commit/e65b4ba</em></a></p><h3>4. Consume the API</h3><p>Time to call the API!</p><p>Add a component that calls the usePets hook and displays their names in a list at src/components/pets/Pets.tsx:</p><pre>import { usePets } from &quot;../../hooks/pet-hooks&quot;;<br><br>export function Pets() {<br>  const { data: pets } = usePets();<br><br>  return (<br>    &lt;ul&gt;<br>      {pets?.map((pet) =&gt; (<br>        &lt;li key={pet.id}&gt;{pet.name}&lt;/li&gt;<br>      ))}<br>    &lt;/ul&gt;<br>  );<br>}</pre><p>and use it inside src/App.tsx:</p><pre>import { QueryClient, QueryClientProvider } from &quot;@tanstack/react-query&quot;;<br>import { Pets } from &quot;./components/pets/Pets&quot;;<br><br>const queryClient = new QueryClient();<br><br>function App() {<br>  return (<br>    &lt;QueryClientProvider client={queryClient}&gt;<br>      &lt;Pets /&gt;<br>    &lt;/QueryClientProvider&gt;<br>  );<br>}<br><br>export default App;</pre><p>Run the development server with pnpm dev, and <em>voilà</em>:</p><figure><img alt="Screenshot of the web browser showing the list of pets’ names retrieved with the API. The devtools are open and show the JSON response for this request." src="https://cdn-images-1.medium.com/max/1024/1*3HBak8NZkPnvsUK7DzmWqQ.png" /></figure><p>The good news is that the Pet Store API works out of the box! This is because a valid server URL is provided inside the OpenAPI file. It is pretty rare to note it.</p><p>In general, your own API is likely deployed on more than one environment (eg: dev, staging, prod), and you often need to configure its base path first (see point 2.3.).</p><p>And that’s if the API is implemented at all! Efficient teams start working on the frontend in parallel with the backend when the API is not yet fully implemented.</p><p>Now for the bad news: the data returned by that live API is full of junk. For instance, pets have poor names, and the categories, tags, and photo URLs are just named <em>‘string’</em>. Developing and styling components requires better data.</p><p>🔎 <em>The source code for step 4 is available at: </em><a href="https://github.com/gfox1984/petstore-app/commit/3ae3fa3"><em>https://github.com/gfox1984/petstore-app/commit/3ae3fa3</em></a></p><h3>5. Mock the API with MSW</h3><p>When the API you depend on is missing or when you want to test your code against specific data, you might want to use a mock server. You could surely hardcode an object and use it in your component, as a temporary fix. But a mock server will let you write entire apps as if you were working with an actual server. This will give you higher confidence in your code and it will also reduce the amount of changes needed when you’re ready to go live.</p><h4>5.1. Install</h4><p>For this, we’ll use the <em>state-of-the-art</em> library for mocking servers called <a href="https://mswjs.io/">Mock Service Worker (MSW)</a>.</p><pre>pnpm add -D msw</pre><p>Next, we need to <a href="https://mswjs.io/docs/integrations/browser#copy-the-worker-script">initialize the project</a>, so that MSW can intercept requests when our app runs in the browser:</p><pre>npx msw init ./public</pre><p>When asked, answer Y to the question. The output should be:</p><pre>Copying the worker script at &quot;&lt;your_path&gt;\petstore-app\public&quot;...<br><br>Worker script successfully copied!<br>  - ./public<br><br>Continue by describing the network in your application:<br><br>https://mswjs.io/docs/getting-started<br><br>      INFO In order to ease the future updates to the worker script,<br>      we recommend saving the path to the worker directory in your package.json.<br>? Do you wish to save &quot;public&quot; as the worker directory? yes<br>Updating &quot;msw.workerDirectory&quot; at &quot;&lt;your_path&gt;\petstore-app\package.json&quot;...</pre><h4>5.2. Configure</h4><p>Now, we’re going to prepare the <a href="https://mswjs.io/docs/network-behavior/rest">list of handlers</a> that will be used to handle requests (empty for now).</p><p>So, go ahead and create src/mocks/handlers/index.ts with:</p><pre>export const handlers = [];</pre><p>Next, create the worker that will intercept those requests, and initialize it with the list of handlers.</p><p>For this, create src/mocks/browser.ts:</p><pre>import { setupWorker } from &quot;msw/browser&quot;;<br>import { handlers } from &quot;./handlers&quot;;<br><br>export const worker = setupWorker(...handlers);</pre><p>Finally, we’ll edit our entry point so that the worker starts with the app. Since it’s a mock server, we’ll do it when the app runs in development mode. In the future, we might want to change that to run <a href="https://vitejs.dev/guide/env-and-mode">based on an environment variable</a> (eg: ENABLE_MOCKING).</p><p>So, edit src/main.tsx and add this block of code to import and start the worker:</p><pre>import React from &quot;react&quot;;<br>import ReactDOM from &quot;react-dom/client&quot;;<br>import App from &quot;./App.tsx&quot;;<br>import &quot;./index.css&quot;;<br><br>if (import.meta.env.DEV) {<br>  const { worker } = await import(&quot;./mocks/browser&quot;);<br>  await worker.start();<br>}<br><br>ReactDOM.createRoot(document.getElementById(&quot;root&quot;)!).render(<br>  &lt;React.StrictMode&gt;<br>    &lt;App /&gt;<br>  &lt;/React.StrictMode&gt;<br>);</pre><p>To validate that everything is configured correctly, run pnpm dev. You should see the following message in the console:</p><figure><img alt="[MSW] Mocking enabled.
[MSW] Warning: intercepted a request without a matching request handler:
 
 • GET http://petstore.swagger.io/v2/pet/findByStatus 

If you still wish to intercept this unhandled request, please create a request handler for it.
Read more: https://mswjs.io/docs/getting-started/mocks" src="https://cdn-images-1.medium.com/max/669/1*6T0p7uoLy41Qy9lFiFz2cQ.png" /></figure><h4>5.3. Mock</h4><p>Now for the interesting part — mocking our API!</p><p>Create some mock Pet objects, and return them when calling the pet/findByStatus endpoint, by pasting this code into a new file at src/mocks/handlers/pet.ts:</p><pre>import { HttpResponse, http } from &quot;msw&quot;;<br>import { Category, Pet, Tag } from &quot;../../api/petstore&quot;;<br><br>// mock categories<br>export const pokemon: Category = { id: 1, name: &quot;Pokemon&quot; };<br><br>// mock tags<br>export const electric: Tag = { id: 1, name: &quot;Electric&quot; };<br>export const fire: Tag = { id: 2, name: &quot;Fire&quot; };<br>export const fyling: Tag = { id: 3, name: &quot;Flying&quot; };<br><br>// mock pets<br>export const pikachu: Pet = {<br>  id: 1,<br>  name: &quot;Pikachu&quot;,<br>  photoUrls: [&quot;https://upload.wikimedia.org/wikipedia/en/a/a6/Pok%C3%A9mon_Pikachu_art.png&quot;],<br>  category: pokemon,<br>  tags: [electric],<br>};<br>export const charizard: Pet = {<br>  id: 2,<br>  name: &quot;Charizard&quot;,<br>  photoUrls: [&quot;https://upload.wikimedia.org/wikipedia/en/1/1f/Pok%C3%A9mon_Charizard_art.png&quot;],<br>  category: pokemon,<br>  tags: [fire, fyling],<br>};<br><br>// mock handlers<br>export const handlers = [<br>  http.get&lt;never, never, Pet[]&gt;(&quot;*/pet/findByStatus&quot;, () =&gt;<br>    HttpResponse.json([pikachu, charizard])<br>  ),<br>];</pre><p>As you can see, each mock object is typed using one of the generated interfaces. The mock handler that intercepts the request is also typed: http.get&lt;never,never,Pet[]&gt;, meaning that it’s a GET method, with:</p><ol><li>no parameters (never)</li><li>no body (never)</li><li>an array of Pets in response (Pet[])</li></ol><p>Although these types are optional, they make sure that we comply with the API specification and help in preventing errors.</p><p>So, we now have mock handlers for our endpoints (well, technically we only have one, but we have to start somewhere!). The next step is to add them to the list at src/mocks/handlers/index.ts:</p><pre>import { handlers as petHandlers } from &quot;./pet&quot;;<br><br>export const handlers = [...petHandlers];</pre><p>When this is done, run the development server (pnpm dev). The app is now showing our mock data!</p><figure><img alt="Screenshot of the browser showing the name of the mock pets in the list. The devtools show the network request with the mock response." src="https://cdn-images-1.medium.com/max/1024/1*IZZj12llNuRuLEI93SbAJw.png" /></figure><p>The cool thing is that mock responses can be inspected directly inside the Network tab using the dev tools as if they were regular responses.</p><h4>5.4. Going further</h4><p>In this article, we’ve mocked a simple GET request. But other methods could be mocked too in a similar way. We could also customize responses based on the parameters we receive in the handler.</p><p>The official documentation has a simple example that implements CRUD operations with mock handlers:</p><p><a href="https://mswjs.io/docs/network-behavior/rest">Describing REST API</a></p><p>🔎 <em>The source code for step 5 is available at: </em><a href="https://github.com/gfox1984/petstore-app/commit/7036eb8"><em>https://github.com/gfox1984/petstore-app/commit/7036eb8</em></a></p><h3>6. Style with Tailwind CSS (optional)</h3><p>This section is optional, so feel free to skip it. But since we’re having fun with our mock data, let’s wrap this up and display those pets in a nicer way!</p><p>Modern apps are built with <a href="https://tailwindcss.com/">Tailwind CSS</a> (controversial). So we’ll configure Tailwind as per <a href="https://tailwindcss.com/docs/guides/vite">their Vite guide</a>, and use it for styling.</p><p>First, add the required libraries, and initialize the project with:</p><pre>pnpm add -D tailwindcss postcss autoprefixer<br>npx tailwindcss init -p</pre><p>Then, update the freshly created tailwind.config.js file with:</p><pre>/** @type {import(&#39;tailwindcss&#39;).Config} */<br>export default {<br>  content: [<br>    &quot;./index.html&quot;,<br>    &quot;./src/**/*.{js,ts,jsx,tsx}&quot;,<br>  ],<br>  theme: {<br>    extend: {},<br>  },<br>  plugins: [],<br>}</pre><p>And finally, replace the content of src/index.css with:</p><pre>@tailwind base;<br>@tailwind components;<br>@tailwind utilities;</pre><p>Tailwind is now set up.</p><p>Let’s update the markup of our Pets component with it! I don’t know about you, but I’m not fluent in Tailwind… So one way to get started is to use <a href="https://v0.dev/">v0 by Vercel</a> to generate some code. Once you have a version you’re happy with (or close enough), you can update the component at src/components/pets/Pets.tsx. Mine looks like this:</p><pre>import { usePets } from &quot;../../hooks/pet-hooks&quot;;<br><br>export function Pets() {<br>  const { data: pets } = usePets();<br>  return (<br>    &lt;ul className=&quot;grid md:grid-cols-3 xl:grid-cols-4 2xl:grid-cols-6 gap-6 m-6&quot;&gt;<br>      {pets?.map((pet) =&gt; (<br>        &lt;li<br>          key={pet.id}<br>          aria-label={pet.name}<br>          className=&quot;group rounded-lg overflow-hidden relative shadow-md&quot;<br>        &gt;<br>          &lt;img<br>            alt={pet.name}<br>            className=&quot;object-contain w-full aspect-[3/2] bg-slate-50&quot;<br>            src={pet.photoUrls[0]}<br>          /&gt;<br>          &lt;div className=&quot;p-4&quot;&gt;<br>            &lt;h3 className=&quot;font-semibold text-lg&quot;&gt;{pet.name}&lt;/h3&gt;<br>            &lt;p className=&quot;text-sm text-gray-500&quot;&gt;{pet.category?.name}&lt;/p&gt;<br>            &lt;div className=&quot;flex gap-2 flex-wrap mt-4&quot;&gt;<br>              {pet.tags?.map((tag) =&gt; (<br>                &lt;span<br>                  key={tag.id}<br>                  className=&quot;rounded-lg bg-gray-100 px-2 py-1 text-xs&quot;<br>                &gt;<br>                  {tag.name}<br>                &lt;/span&gt;<br>              ))}<br>            &lt;/div&gt;<br>          &lt;/div&gt;<br>        &lt;/li&gt;<br>      ))}<br>    &lt;/ul&gt;<br>  );<br>}</pre><p>Note that I also added some aria-label for accessibility (see <a href="https://medium.com/@gfox1984/accessibility-the-selfish-reasons-why-developers-should-care-about-it-b02bbd6a896c">Accessibility: the selfish reasons why developers should care about it</a>).</p><p>Now the page should look something like this:</p><figure><img alt="Screenshot of the web browser showing a much nicer list of pets called Pokemon and Charizard, complete with picture, category and tags." src="https://cdn-images-1.medium.com/max/1024/1*vcgeOFewJHA0GleF1wzibQ.png" /></figure><p>Better 😌</p><p>🔎 <em>The source code for step 6 is available at: </em><a href="https://github.com/gfox1984/petstore-app/commit/da52b5e"><em>https://github.com/gfox1984/petstore-app/commit/da52b5e</em></a></p><h3>7. Test with Vitest and Testing Library</h3><p>Testing time! The part you’ve all been waiting for!</p><p>More seriously: we’ve built a strong, fully-typed app. What we need now are strong tests that will ensure:</p><ol><li>The application behaves as expected</li><li>We have a safety net when we refactor</li></ol><p>I know that some people have an allergic reaction to testing. Maybe they had a bad experience in the past. Maybe they don’t want to invest time in testing. Today I want to show you that testing doesn’t have to be time-consuming or painful.</p><h4>7.1. Install</h4><p>Let’s start with the setup, which is the <strong>painful</strong> part… But luckily, you only need to do it once.</p><p>I decided to use <a href="https://vitest.dev/">Vitest</a> instead of Jest. This will actually take a lot of the pain away, with <a href="https://vitest.dev/guide/migration.html#migrating-from-jest">minimal trade-offs</a>… We’ll also use <a href="https://testing-library.com/">Testing Library</a> to help us write <em>good</em> tests.</p><p>For this, add the following dev dependencies:</p><pre>pnpm add -D vitest @testing-library/react @testing-library/jest-dom @types/react-dom jsdom</pre><p>Next, add a test script to package.json:</p><pre>&quot;scripts&quot;: {<br>  ...<br>  &quot;test&quot;: &quot;vitest&quot;<br>}</pre><p>Finally, edit vite-config.ts with:</p><pre>/// &lt;reference types=&quot;vitest&quot; /&gt;<br>// ...<br>export default defineConfig({<br>  // ...<br>  test: {<br>    environment: &quot;jsdom&quot;,<br>    setupFiles: [&quot;./src/vitest-setup.ts&quot;],<br>  },<br>});</pre><p>and create src/vitest-setup.ts:</p><pre>import &quot;@testing-library/react&quot;;<br>import &quot;@testing-library/jest-dom/vitest&quot;;<br><br>import { afterEach } from &quot;vitest&quot;;<br>import { cleanup } from &quot;@testing-library/react&quot;;<br><br>afterEach(() =&gt; {<br>  cleanup();<br>});</pre><h4>7.2. Configure</h4><p>The secret of <em>good</em> tests is a good configuration. One that will make our tests easy to write and to maintain, while staying as close as possible to reality.</p><p>In our app, like in most apps, we use a bunch of top-level providers to configure the tools we use in the components. Our app is still small, and it only uses one provider: QueryClientProvider. Yet, if we want the tested components to run in the same conditions as in the app, we need to make sure that the test suite is configured to use such providers.</p><p>In its <a href="https://testing-library.com/docs/react-testing-library/setup/#custom-render">setup docs</a>, Testing Library advises replacing the built-in render function with a custom one, which wraps the tested component with our providers. So let’s add a QueryClientProvider to our custom render function, which we’ll set up according to <a href="https://tanstack.com/query/latest/docs/framework/react/guides/testing">TanStack Query Docs</a>.</p><p>Create a src/utils/test-utils.tsx with this code:</p><pre>import React, { ReactElement } from &quot;react&quot;;<br>import { render, RenderOptions } from &quot;@testing-library/react&quot;;<br>import { QueryClient, QueryClientProvider } from &quot;@tanstack/react-query&quot;;<br><br>const AllTheProviders = ({ children }: { children: React.ReactNode }) =&gt; {<br>  const queryClient = new QueryClient({<br>    defaultOptions: {<br>      queries: {<br>        retry: false,<br>      },<br>    },<br>  });<br>  return (<br>    &lt;QueryClientProvider client={queryClient}&gt;{children}&lt;/QueryClientProvider&gt;<br>  );<br>};<br><br>const customRender = (<br>  ui: ReactElement,<br>  options?: Omit&lt;RenderOptions, &quot;wrapper&quot;&gt;<br>) =&gt; render(ui, { wrapper: AllTheProviders, ...options });<br><br>export * from &quot;@testing-library/react&quot;;<br>export { customRender as render };</pre><p>Finally, we also want to make sure that the mock server we created earlier is used to respond to network requests in the test suite. To do that, simply create a src/mocks/node.ts file with:</p><pre>import { setupServer } from &quot;msw/node&quot;;<br>import { handlers } from &quot;./handlers&quot;;<br><br>export const server = setupServer(...handlers);</pre><p>Then, add this setup + cleanup code at the end of src/vitest-setup.ts:</p><pre>// ...<br>import { beforeAll, afterEach, afterAll } from &quot;vitest&quot;;<br>import { server } from &quot;./mocks/node&quot;;<br><br>// ...<br>beforeAll(() =&gt; server.listen());<br>afterEach(() =&gt; server.resetHandlers());<br>afterAll(() =&gt; server.close());</pre><p>We can now throw any component into the test suite and expect it to work out of the box. This configuration will dramatically reduce the number of mocks we use in our tests and, by extension, those tests will also be less brittle.</p><h4>7.3. Test components</h4><p>Enough talking, let’s write some tests for our Pets component!</p><p>Add a src/components/Pets.test.tsx file with this test case to ensure our component renders:</p><pre>import { it } from &quot;vitest&quot;;<br>import { Pets } from &quot;./Pets&quot;;<br>import { render } from &quot;../../utils/test-utils&quot;;<br><br>it(&quot;renders&quot;, () =&gt; {<br>  render(&lt;Pets /&gt;);<br>});</pre><p>Run it with pnpm test and check the terminal:</p><pre>✓ src/components/pets/Pets.test.tsx (1)<br>   ✓ renders<br> <br> Test Files  1 passed (1)<br>      Tests  1 passed (1)</pre><p>The test passes 😅!</p><p>Nice work, but a bit too easy. We want to make sure it renders the right things! Since we’ve configured our mock server to return some pre-defined pets, we want to assert that those pets are effectively rendered.</p><p>For this, add a new test case to the file:</p><pre>import { expect, it } from &quot;vitest&quot;;<br>import { Pets } from &quot;./Pets&quot;;<br>import { render, screen } from &quot;../../utils/test-utils&quot;;<br><br>it(&quot;renders&quot;, () =&gt; {<br>  render(&lt;Pets /&gt;);<br>});<br><br>it(&quot;shows the list of pets&quot;, async () =&gt; {<br>  render(&lt;Pets /&gt;);<br>  expect(screen.getByRole(&quot;listitem&quot;, { name: &quot;Pikachu&quot; })).toBeInTheDocument();<br>  expect(screen.getByRole(&quot;listitem&quot;, { name: &quot;Charizard&quot; })).toBeInTheDocument();<br>});</pre><p>Run it and…</p><pre>FAIL  src/components/pets/Pets.test.tsx &gt; shows the list of pets<br>TestingLibraryElementError: Unable to find an element with the text: Pikachu. This could be because the text is broken up by multiple elements. In this case, you can provide a function for your text matcher to make your matcher more flexible.</pre><p>…FAIL!? 😱</p><p>Do you get why?</p><p>Our test suite is configured to behave like the real application. So just like in real life, network requests are asynchronous. Therefore, we need to wait until the request has completed before asserting on the rendered pets.</p><p>That’s for the technical explanation. In practice, the network request is an implementation detail. And you don’t want to test against an implementation detail (unless you want to end up hating tests!). So what you do instead is wait until the expected element appears on screen to make the assertion. Testing Library provides an <a href="https://testing-library.com/docs/guide-disappearance#1-using-findby-queries">asynchronous </a><a href="https://testing-library.com/docs/guide-disappearance#1-using-findby-queries">findBy query</a> for that.</p><p>So, to make the test pass, prefix the test function with async and replace the first occurrence of screen.getByRole with await screen.findByRole:</p><pre>it(&quot;shows the list of pets&quot;, async () =&gt; {<br>  render(&lt;Pets /&gt;);<br>  expect(await screen.findByRole(&quot;listitem&quot;, { name: &quot;Pikachu&quot; })).toBeInTheDocument();<br>  expect(screen.getByRole(&quot;listitem&quot;, { name: &quot;Charizard&quot; })).toBeInTheDocument();<br>});</pre><p>Now the test passes 🎉</p><pre> ✓ src/components/pets/Pets.test.tsx (2)<br>   ✓ renders<br>   ✓ shows the list of pets<br><br> Test Files  1 passed (1)<br>      Tests  2 passed (2)</pre><h4>7.4. Test hooks (optional)</h4><p>Depending on your testing strategy, you might also want to check that your custom hooks return the expected data. This is not strictly needed since the previous test already covers this (test coverage will confirm this). But for the sake of this exercise, let’s add a test case for the usePets hook.</p><p>Testing Library has <a href="https://testing-library.com/docs/solid-testing-library/api/#renderhook">a </a><a href="https://testing-library.com/docs/solid-testing-library/api/#renderhook">renderHook function</a> for this, but we don’t want to use it directly. Instead, we’ll modify src/utils/test-utils.ts and replace the renderHook function with a custom one that wraps the tested hook with our providers, just as we did earlier with the render function:</p><pre>import { render, renderHook, RenderOptions } from &quot;@testing-library/react&quot;;<br><br>//...<br><br>const customRenderHook = &lt;Result, Props&gt;(<br>  render: (initialProps: Props) =&gt; Result,<br>  options?: Omit&lt;RenderOptions, &quot;wrapper&quot;&gt;<br>) =&gt; renderHook(render, { wrapper: AllTheProviders, ...options });<br><br>//...<br><br>export { customRenderHook as renderHook };</pre><p>We are now ready to assert that our usePets hook returns the expected data. Following <a href="https://tanstack.com/query/latest/docs/framework/react/guides/testing">the recommendations from React Query</a> (which we use in the hook), let’s create a src/hooks/pet-hooks.ts file with:</p><pre>import { describe, expect, it } from &quot;vitest&quot;;<br>import { renderHook, waitFor } from &quot;../utils/test-utils&quot;;<br>import { usePets } from &quot;./pet-hooks&quot;;<br>import { charizard, pikachu } from &quot;../mocks/handlers/pet&quot;;<br><br>describe(&quot;usePets&quot;, () =&gt; {<br>  it(&quot;returns a list of pets&quot;, async () =&gt; {<br>    const { result } = renderHook(() =&gt; usePets());<br>    await waitFor(() =&gt; expect(result.current.isSuccess).toBe(true));<br>    expect(result.current.data).toEqual([pikachu, charizard]);<br>  });<br>});</pre><p>And then run the tests again :</p><pre> ✓ src/hooks/pet-hooks.test.ts (1)<br> ✓ src/components/pets/Pets.test.tsx (2)<br><br> Test Files  2 passed (2)<br>      Tests  3 passed (3)</pre><p>All pass!</p><p>Testing hooks can be useful when you want to cover some particular use cases or edge cases that you don’t necessarily verify when testing your components.</p><p>🔎 <em>The source code for step 7 is available at: </em><a href="https://github.com/gfox1984/petstore-app/commit/edd8454"><em>https://github.com/gfox1984/petstore-app/commit/edd8454</em></a></p><h3>Conclusion</h3><p>I hope that you enjoyed this tutorial and that it will help you make the most of OpenAPI. The topics covered here can significantly benefit your team by allowing it to start working on the frontend code sooner than previously possible. No more waiting for the backend to be ready!</p><p>As a developer, fully typing your client API is not only a great way to reduce bugs, but it will also boost your productivity thanks to better code completion. Additionally, it will simplify testing if you invest in a good setup.</p><p>Applications that are fully typed and tested not only stand the test of time better but also provide greater assurance when it comes to refactoring.</p><h3>Coming up in Part 3</h3><p>Rejoice! There will be a Part 3!</p><p>Generating a good client API requires a good specification in the first place, so I’ll share some advice to optimize your OpenAPI file for optimal code generation. Plus, I’ll provide tips to enhance your developer experience and more!</p><p>You know what to do: <strong>follow</strong> me and stay tuned!</p><h3>Stackademic 🎓</h3><p>Thank you for reading until the end. Before you go:</p><ul><li>Please consider <strong>clapping</strong> and <strong>following</strong> the writer! 👏</li><li>Follow us <a href="https://twitter.com/stackademichq"><strong>X</strong></a> | <a href="https://www.linkedin.com/company/stackademic"><strong>LinkedIn</strong></a> | <a href="https://www.youtube.com/c/stackademic"><strong>YouTube</strong></a> | <a href="https://discord.gg/in-plain-english-709094664682340443"><strong>Discord</strong></a></li><li>Visit our other platforms: <a href="https://plainenglish.io/"><strong>In Plain English</strong></a> | <a href="https://cofeed.app/"><strong>CoFeed</strong></a> | <a href="https://differ.blog/"><strong>Differ</strong></a></li><li>More content at <a href="https://stackademic.com/"><strong>Stackademic.com</strong></a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=6ae1b8c6de92" width="1" height="1" alt=""><hr><p><a href="https://blog.stackademic.com/fully-typed-web-apps-with-openapi-part-2-6ae1b8c6de92">Fully typed Web Apps with OpenAPI (Part 2)</a> was originally published in <a href="https://blog.stackademic.com">Stackademic</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How I Stay Updated with React in 2024]]></title>
            <link>https://medium.com/@gfox1984/how-i-stay-updated-with-react-in-2024-463a729e7f6e?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/463a729e7f6e</guid>
            <category><![CDATA[front-end-development]]></category>
            <category><![CDATA[software-engineering]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[javascript]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Thu, 21 Mar 2024 19:02:21 GMT</pubDate>
            <atom:updated>2024-08-06T13:24:22.370Z</atom:updated>
            <content:encoded><![CDATA[<h4>It’s 2024, RSC is all over the place and React 19 is coming. Get ready for it!</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*21iX6ocJgCAp1MceQH0bpQ.png" /><figcaption>Image by author — The logos used in the illustration are trademarks of 𝕏 and Meta</figcaption></figure><p>About two years ago, I wrote a story on Medium about how I stay updated with React (and frontend development in general), which proved fairly popular:</p><p><a href="https://medium.com/@gfox1984/how-i-stay-up-to-date-with-react-and-front-end-development-d72efb6d6f59">How I Stay Up to Date with React (and Front-End Development)</a></p><p>While the story is still relevant today, I felt that it needed a refresh. I’m now following more people on Twitter, and I use some resources more than others.</p><p>Also, 2024 promises to be a big year: <a href="https://react.dev/blog">React is due to receive some upgrades</a>, more frameworks are starting to adopt React Server Components (RSC), and there is now a consensus that <a href="https://react.dev/learn/you-might-not-need-an-effect">useEffect should be avoided like the plague</a>. Oh, and what about AI? Vercel has been <a href="https://v0.dev/">blowing our minds with v0</a> and <a href="https://sdk.vercel.ai/docs/concepts/ai-rsc">Generative UI</a> since the year started.</p><p>So, if you don’t want to be left out, I have compiled a list of resources that will help you stay in the loop:</p><h3>T̵w̵i̵t̵t̵e̵r̵ 𝕏</h3><p>𝕏 is more than ever the place to be if you want to stay updated with React. The great exodus announced when Elon Musk bought the social network never happened. I even had to add a “bonus” section to my top 5 because of the amount and quality of influential developers on the platform:</p><h4>Dan Abramov²</h4><iframe src="https://cdn.embedly.com/widgets/media.html?type=text%2Fhtml&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;schema=twitter&amp;url=https%3A//twitter.com/dan_abramov2/status/1760524200995868854%3Fs%3D20&amp;image=" width="500" height="281" frameborder="0" scrolling="no"><a href="https://medium.com/media/52f28cd782a920775ab3aa121dbf7cbe/href">https://medium.com/media/52f28cd782a920775ab3aa121dbf7cbe/href</a></iframe><p>Nobody knows what happened to Dan the 1st. He became invisible on 𝕏 after he quit Meta. But fear not, he’s back with Dan 2, stronger than ever. Dan is on a mission to educate people on React Server Components (RSC). And if there’s one person on Earth that can do that, it’s him.</p><h4>Kent C Dodds</h4><iframe src="https://cdn.embedly.com/widgets/media.html?type=text%2Fhtml&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;schema=twitter&amp;url=https%3A//twitter.com/kentcdodds/status/1773511255309144338%3Fs%3D20&amp;image=" width="500" height="281" frameborder="0" scrolling="no"><a href="https://medium.com/media/4480947a23b8d6b673214b3e3bc82c69/href">https://medium.com/media/4480947a23b8d6b673214b3e3bc82c69/href</a></iframe><p>Kent is one of the most experienced React developers out there. He’s produced an insane amount of training content, worked at PayPal and Google, and co-founded <a href="https://www.bing.com/ck/a?!&amp;&amp;p=5b1326d4b1f2a251JmltdHM9MTcxMDExNTIwMCZpZ3VpZD0zZDE4OGU0Yi02MTQ4LTY2OTMtMmNlNy05Y2FmNjA0ZDY3NGUmaW5zaWQ9NTIwNA&amp;ptn=3&amp;ver=2&amp;hsh=3&amp;fclid=3d188e4b-6148-6693-2ce7-9caf604d674e&amp;psq=remix+web&amp;u=a1aHR0cHM6Ly9yZW1peC5ydW4v&amp;ntb=1">Remix</a>. He’s even managed to make developers enjoy testing web apps (partly thanks to <a href="https://testing-library.com/">Testing Library</a>).</p><h4>Dominik Dorfmeister</h4><h3>Dominik 🔮 on Twitter: &quot;react-query doesn&#39;t do data-fetching - it&#39;s an async state manager based on promises, so you don&#39;t need to copy data from react-query into another state manager. I have multiple blogposts and a talk on just this topic 😂 / Twitter&quot;</h3><p>react-query doesn&#39;t do data-fetching - it&#39;s an async state manager based on promises, so you don&#39;t need to copy data from react-query into another state manager. I have multiple blogposts and a talk on just this topic 😂</p><p>Everybody knows and loves React Query! Dominik is the guy behind it. He’s very active on 𝕏 and has a great blog too: <a href="https://tkdodo.eu/blog/all">https://tkdodo.eu/</a>.</p><h4>Cory House</h4><h3>Cory House on Twitter: &quot;React has over a dozen popular third-party state libraries. React Query, Zustand, Redux, Jotai, Mobx, etc.Prediction: Third-party React state libraries will become less relevant in 2024.Why? I see two things &quot;eating&quot; this space:1. Routers are becoming state managers.... / Twitter&quot;</h3><p>React has over a dozen popular third-party state libraries. React Query, Zustand, Redux, Jotai, Mobx, etc.Prediction: Third-party React state libraries will become less relevant in 2024.Why? I see two things &quot;eating&quot; this space:1. Routers are becoming state managers....</p><p>I didn’t even realize how much I liked Cory’s tweets until I looked at my list of bookmarks. Cory is an independent consultant. His tweets are spot-on and full of good advice!</p><h4>Theo Browne</h4><h3>Theo - t3.gg on Twitter: &quot;If you really need to build a &quot;Single Page App&quot; and can&#39;t server render for some reason, this is probably your best bet https://t.co/b9C92VRLtL / Twitter&quot;</h3><p>If you really need to build a &quot;Single Page App&quot; and can&#39;t server render for some reason, this is probably your best bet https://t.co/b9C92VRLtL</p><p>Theo is more well-known for this YouTube channel (read on), but his 𝕏 account is well worth it too—a mix of amusing shit posts and overreactions to the latest news in the frontend world. But behind the agitation, Theo is an amazing hardworking React developer.</p><h4>Bonus Track</h4><p>OK, I could go on like this forever. Tech Twitter is the place to be when you’re a frontend developer. But let’s cut it short and wrap this with a few more people.</p><p>Whether you like <a href="https://nextjs.org/">Next.js</a> or not, Vercel is always going to be on top of the React game, and its CEO, <a href="https://twitter.com/rauchg">Guillermo Rauch</a>, is here to prove it. But maybe you’re more into <a href="https://remix.run/">Remix</a>? Then <a href="https://twitter.com/ryanflorence">Ryan Florence</a> is your guy. And what about <a href="https://twitter.com/tannerlinsley">Tanner Linsley</a>, creator of the <a href="https://tanstack.com/">TanStack</a>? Let’s not forget <a href="https://twitter.com/dai_shi">Daishi Kato</a>, the author of the only state libraries you want to use in 2024 (<a href="https://zustand-demo.pmnd.rs/">Zustand</a>, <a href="https://jotai.org/">Jotai</a>,…). Finally, I wanted to mention <a href="https://twitter.com/mattpocockuk">Matt Pocock</a>, the TypeScript wizard (he also has <a href="https://www.totaltypescript.com/newsletter">a newsletter</a> and <a href="https://www.totaltypescript.com/tutorials">free tutorials</a>).</p><p><em>Okay, I couldn’t conclude without mentioning </em><a href="https://twitter.com/0xca0a"><em>@0xca0a (</em><strong><em>☄︎</em></strong><em>)</em></a><em>… His tweets probably won’t help you pay your bills (who knows), but you‘ll be amazed to see what </em><a href="https://docs.pmnd.rs/react-three-fiber/getting-started/introduction"><em>React Three Fiber</em></a><em> can achieve:</em></p><iframe src="https://cdn.embedly.com/widgets/media.html?type=text%2Fhtml&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;schema=twitter&amp;url=https%3A//twitter.com/0xca0a/status/1749879826201170226%3Fs%3D20&amp;image=" width="500" height="281" frameborder="0" scrolling="no"><a href="https://medium.com/media/93ee70f70ea0791fdeacecf71c7bc4ee/href">https://medium.com/media/93ee70f70ea0791fdeacecf71c7bc4ee/href</a></iframe><h3>Web</h3><p>I always love a good read and I like to learn by the book. Here are my few favorite websites for that:</p><h4>React.dev</h4><p><a href="https://react.dev/">React</a></p><p>This may be obvious, but the best resource to learn React is the official React website… The <a href="https://react.dev/learn">Learn section</a> contains plenty of interactive examples, and I cannot think of a better way to learn React. The website is accessible to all levels, from beginner to advanced developers.</p><p>Once you’ve read and understood the <a href="https://react.dev/learn/escape-hatches">advanced topics</a>, you will probably be better than 80% of the React developers out there. Dan Abramov himself wrote these docs, so it’s a joy to read and you’ll easily get there if you’re motivated enough.</p><p>Also, don’t forget to keep an eye on the <a href="https://react.dev/blog">blog posts</a> to know what’s cooking.</p><h4>Frontend Mastery</h4><p><a href="https://frontendmastery.com/posts/navigating-the-future-of-frontend/">Navigating the future of frontend</a></p><p>I’m so glad I found this website back in 2022 in my original story. Frontend Mastery has helped me navigate through what was happening in the frontend world since then, and in particular with how to manage state and implement composition.</p><p>It’s easy to fall for biased fanboy opinions when you read tweets and blog posts (although this is also part of the fun 😁). But Frontend Mastery always keeps a humble, high-level, detached eye towards frontend development. Of course, React will always have a good spot on Frontend Mastery, but so far, it has never been promoted king.</p><p>I highly recommend this website if you like deep dives and you want to up your game.</p><h4>Epic Web</h4><p><a href="https://www.epicweb.dev/articles">Epic Web Dev Articles</a></p><p>Epic Web aims to teach the world about modern full-stack web development. Since it’s Kent C Dodds’ project, you already know it’s going to be good! But this time, he has teamed up with several top developers, such as <a href="https://twitter.com/kettanaito">Artem Zakharchenko</a> (creator of <a href="https://mswjs.io/">MSW</a>), to produce amazing content.</p><p>The articles, as well as some tutorials, are free to access. But in case you or your employer have a thousand bucks to spare, consider buying the full course (I haven’t yet, but I’m working on it 😉).</p><h3>Newsletters</h3><p>Newsletters are a great way to stay updated and discover things you may not have thought of. Here my favorite two:</p><h4>Bytes: Your Weekly Dose of JavaScript</h4><p><a href="https://bytes.dev/">Join Bytes - Your weekly dose of JavaScript</a></p><p>One of the best JavaScript newsletters! Bytes sends its newsletter twice a week, which is A LOT of content to digest. But luckily, the newsletter is an easy read and it never fails to make me smile.</p><p>Here is an extract from their latest “OSScars” winners:</p><blockquote>Best Independent Film: htmx. Like most indie films, everyone loves to talk about how great htmx is, despite the fact that they’ve never actually used it. That’s how you know the htmx team’s extended PSYOP <em>meme-based marketing campaign</em> is working.</blockquote><p>And if you’ve missed the newsletter or you’re just curious about it, see the <a href="https://bytes.dev/archives">Bytes Archive</a>.</p><h4>Medium</h4><p>Of course, I had to include Medium! You found my story on Medium, and I bet it’s not your first time here. The platform has a lot of stories written by authors like you and me who like to share their knowledge on the technologies they work with.</p><p>Some content can only be found on Medium. For example, since I’ve had to work with Micro-frontends and Module Federation, I found that the main contributor in the domain, <a href="https://medium.com/u/9ef1379caffc">Zack Jackson</a>, publishes all his updates and tutorials here.</p><p>Many major companies, such as <a href="https://medium.com/u/c3aeaf49d8a4">Netflix Technology Blog</a>, also publish their blog on Medium. I find it always interesting to read about how big corps do technology at scale.</p><p>So make sure to follow the authors you like and subscribe to the newsletter. The newsletter is tailored to you, with the people and topics you like. The more you read, the better it gets.</p><h3>Video</h3><p>I’ll be honest, I don’t watch much video as I find it hard to sit in front of a screen for 10+ minutes without distraction. But I happen to watch some, and I know many of you like this format. So here it is:</p><h4>Theo Browne</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2Fvxkbf5QMA2g%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3Dvxkbf5QMA2g&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2Fvxkbf5QMA2g%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/13240e0a36dbaed8f627d2092c162cb4/href">https://medium.com/media/13240e0a36dbaed8f627d2092c162cb4/href</a></iframe><p>Yes, Theo, again. This time, in his natural habitat: YouTube. If anything happens in the React World, you’ll probably see a video of Theo about it in the next 48 hours (or less). You might find that a few of his videos are just plain rants, but he always has a point, whether you agree or not.</p><h4>Jack Herrington</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FqOwnQJOClrw%3Ffeature%3Doembed&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DqOwnQJOClrw&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FqOwnQJOClrw%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/6d0e50a91e46ee00fe3fd2649da70621/href">https://medium.com/media/6d0e50a91e46ee00fe3fd2649da70621/href</a></iframe><p>Jack is the other big name on YouTube if you like frontend development. Just like Theo, Jack covers all the latest technologies, with a hands-on approach. He’s also an expert on some niches, such as Micro Frontends, and his content has become the reference in the domain.</p><h4>Ben Holmes</h4><iframe src="https://cdn.embedly.com/widgets/media.html?src=https%3A%2F%2Fwww.youtube.com%2Fembed%2FMaebEqhZR84%3Fstart%3D422%26feature%3Doembed%26start%3D422&amp;display_name=YouTube&amp;url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DMaebEqhZR84&amp;image=https%3A%2F%2Fi.ytimg.com%2Fvi%2FMaebEqhZR84%2Fhqdefault.jpg&amp;key=a19fcc184b9711e1b4764040d3dc5c07&amp;type=text%2Fhtml&amp;schema=youtube" width="854" height="480" frameborder="0" scrolling="no"><a href="https://medium.com/media/6d3c7fca204cf3688260348ba36cafb7/href">https://medium.com/media/6d3c7fca204cf3688260348ba36cafb7/href</a></iframe><p>He’s one of the new kids on the block. Don’t be misled by his juvenile look, Ben is a lead engineer at <a href="https://astro.build/">Astro</a> and he probably knows more React than we do. He’s published some impressive videos on React Server Components, but Ben’s also posting some shorts on <a href="https://www.youtube.com/@bholmesdev/shorts">YouTube</a> and <a href="https://www.tiktok.com/@bholmesdev">TikTok</a> if you just want your daily fix.</p><h3>Conclusion</h3><p>If you made it up here, you might feel a bit overwhelmed! Don’t worry, it’s normal, nobody can’t digest that amount of information. What matters is that you find your way and frequency to stay informed. Don’t be pressured to try out or use everything you read about. Let the hype settle and see what sticks.</p><p>You’ll be amazed to see what a few minutes spent reading each week will do to you. Things will start picking your curiosity. Without noticing, you will learn and improve your skills, and be more prepared for the next big thing to happen.</p><p>Finally, remember to share your best finds with your peers! It’s easier and more fun when you have a community around you.</p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=463a729e7f6e" width="1" height="1" alt="">]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Fully typed Web Apps with OpenAPI (Part 1)]]></title>
            <link>https://blog.stackademic.com/fully-typed-web-apps-with-openapi-part-1-595d55766670?source=rss-c0c83930ac5f------2</link>
            <guid isPermaLink="false">https://medium.com/p/595d55766670</guid>
            <category><![CDATA[software-architecture]]></category>
            <category><![CDATA[typescript]]></category>
            <category><![CDATA[open-api]]></category>
            <category><![CDATA[react]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Guillaume Renard]]></dc:creator>
            <pubDate>Wed, 06 Mar 2024 17:06:32 GMT</pubDate>
            <atom:updated>2024-08-09T00:40:18.646Z</atom:updated>
            <content:encoded><![CDATA[<h4>Meet OpenAPI generator, the unsung hero for fully typed APIs.</h4><figure><img alt="A superhero with a cape and a mask on his eyes, with colors light green, white, and dark gray, turning his back on the camera, who is a developer and not very muscular." src="https://cdn-images-1.medium.com/max/1024/1*1J7G26MIYjxwwABcsTZvQQ.png" /><figcaption>Image generated with AI (Microsoft Copilot)</figcaption></figure><h3>Challenges &amp; solutions</h3><p>React legend <a href="https://twitter.com/kentcdodds">Kent C. Dodds</a> wrote about the challenges and benefits of <a href="https://www.epicweb.dev/fully-typed-web-apps">fully typed web apps</a> in the Epic Web series. If you haven’t read it yet, you should give it a go, it’s well worth it.</p><p>One of the biggest pain points in web development is making network requests and processing their response. In the past years, new technologies have emerged to make this task easier and safer.</p><p>For example, if you use TypeScript in both your frontend and backend, then <a href="https://trpc.io/">tRPC</a> is a great candidate to share your types between the two. tRPC has been popularized by the <a href="https://create.t3.gg/">T3 Stack</a> created by the famous Youtuber <a href="https://twitter.com/t3dotgg">Theo</a>.</p><p><a href="https://zod.dev/">Zod</a> is another library that is commonly used with <a href="https://trpc.io/">tRPC</a>, which you can also use on its own. Zod allows you to define schemas to validate your data and infer types. This is great for validating the input and output of your APIs and keeping your app fully typed.</p><h3>What about OpenAPI?</h3><p>All of this is great, but if you’ve worked in a medium or a large company, you know that the backend and frontend are often developed by different developers, using different technologies. So forget tRPC. Maybe you could use Zod. But chances are your company already uses <a href="https://www.openapis.org/what-is-openapi">OpenAPI</a> (<a href="https://www.openapis.org/faq">formerly known as Swagger</a>) to document its services.</p><p>Yes, OpenAPI documentation looks great and can be understood by all the stakeholders, whether they are technical people or not. But OpenAPI can do much more. Yet, only a few companies are making the most of it.</p><p>How many frontend developers out there are still translating OpenAPI docs to code by hand? Not only that is tedious, but it is also very error-prone. Surely there must be a better way!</p><h3>Introducing OpenAPI generator</h3><p>Enter the magical world of <a href="https://openapi-generator.tech/">OpenAPI generator</a>! Generators allow you to generate a variety of contents from your OpenAPI documents:</p><ul><li><a href="https://openapi-generator.tech/docs/generators#client-generators">Client code</a> that you can use to consume a service</li><li><a href="https://openapi-generator.tech/docs/generators#server-generators">Server code</a> that you can use to implement a service</li><li>Human-friendly <a href="https://openapi-generator.tech/docs/generators#documentation-generators">documentation</a></li><li><a href="https://openapi-generator.tech/docs/generators#schema-generators">Schemas</a> used to generate a database or describe a service</li></ul><p>In this article, we’ll focus on client generators.</p><h3>Example</h3><p>Let’s use a concrete example. Imagine that you are the owner of an online pet store and that your service is described in this OpenAPI document: <a href="https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml">petstore.yaml</a>. If you’ve read some OpenAPI documentation before, you won’t find it very original… But it is still a good example.</p><p>This document defines a few operations: add a pet, update its details, create orders, manage users, etc… Even for the non-initiated, the OpenAPI format is fairly easy to read and understand. That’s why developers and stakeholders like it so much.</p><p>For example, here is the definition of the endpoint used to add a pet to the store:</p><pre>paths:<br>  /pet:<br>    post:<br>      tags:<br>        - pet<br>      summary: Add a new pet to the store<br>      description: &#39;&#39;<br>      operationId: addPet<br>      responses:<br>        &#39;200&#39;:<br>          description: successful operation<br>          content:<br>            application/xml:<br>              schema:<br>                $ref: &#39;#/components/schemas/Pet&#39;<br>            application/json:<br>              schema:<br>                $ref: &#39;#/components/schemas/Pet&#39;<br>        &#39;405&#39;:<br>          description: Invalid input<br>      security:<br>        - petstore_auth:<br>            - &#39;write:pets&#39;<br>            - &#39;read:pets&#39;<br>      requestBody:<br>        $ref: &#39;#/components/requestBodies/Pet&#39;</pre><p>and the definition of the data it refers to:</p><pre>components:<br>  requestBodies:<br>    Pet:<br>      content:<br>        application/json:<br>          schema:<br>            $ref: &#39;#/components/schemas/Pet&#39;<br>        application/xml:<br>          schema:<br>            $ref: &#39;#/components/schemas/Pet&#39;<br>      description: Pet object that needs to be added to the store<br>      required: true<br>  schemas:<br>    Pet:<br>      title: a Pet<br>      description: A pet for sale in the pet store<br>      type: object<br>      required:<br>        - name<br>        - photoUrls<br>      properties:<br>        id:<br>          type: integer<br>          format: int64<br>        category:<br>          $ref: &#39;#/components/schemas/Category&#39;<br>        name:<br>          type: string<br>          example: doggie<br>        photoUrls:<br>          type: array<br>          xml:<br>            name: photoUrl<br>            wrapped: true<br>          items:<br>            type: string<br>        # more...</pre><p>As a frontend developer, at this point, you could roll up your sleeves, grab a coffee, and start translating this document into code. With a fair amount of copy/pasting, you should get the job done in a few hours… not to mention the cost to your sanity and the few mistakes nobody spotted on the way.</p><p>Or you could use a generator.</p><h3>From documentation to Code!</h3><p>Let’s start with the unpleasant part… OpenAPI generator is implemented in Java (🤷), so you first need to install Java on your machine. But beware, there is a catch… The latest OpenAPI generator requires Java 11, which only comes with the <a href="https://www.oracle.com/java/technologies/downloads">full Java Development Kit (JDK)</a> (160MB+) (<a href="https://stackoverflow.com/questions/53111921/how-can-i-get-java-11-run-time-environment-working-since-there-is-no-more-jre-11">Oracle stopped shipping the lightweight Java Runtime (JRE) at version 8</a> (🤷🤷).</p><p>Once you’re done, you can either <a href="https://openapi-generator.tech/docs/installation#jar">stick with Java</a> or use your favorite package manager to run the generator. My preferred option is to use a package manager, as it will allow us to integrate the generator into our existing projects later on.</p><p>The first step is to install the CLI package:</p><pre>npm install @openapitools/openapi-generator-cli -g</pre><p>Then, run it on the downloaded API, with the chosen generator. Let’s say our project uses <a href="https://axios-http.com/">Axios</a> and TypeScript. We can use the following command line to generate the code into the petstore folder:</p><pre>npx @openapitools/openapi-generator-cli generate -i petstore.yaml -g typescript-axios -o petstore</pre><p>And if you don’t like Axios or Typescript (🤔), there are other generators available, see <a href="https://openapi-generator.tech/docs/generators#client-generators">the complete list here</a>.</p><p>The generated petstore folder contains a bunch of files:</p><pre>.openapi-generator<br>.gitignore<br>.npmignore<br>.openapi-generator-ignore<br>api.ts<br>base.ts<br>common.ts<br>configuration.ts<br>git_push.sh<br>index.ts</pre><p>The interesting part is located inside api.ts. The other files can be ignored for now (*ignore and .sh files aren’t useful unless you want to publish the folder to its own repo, and the other files only contain utility code).</p><p>If you open api.ts in your editor, you’ll see plenty of well-documented TypeScript interfaces, such as:</p><pre>/**<br> * A pet for sale in the pet store<br> * @export<br> * @interface Pet<br> */<br>export interface Pet {<br>    /**<br>     * <br>     * @type {number}<br>     * @memberof Pet<br>     */<br>    &#39;id&#39;?: number;<br>    /**<br>     * <br>     * @type {Category}<br>     * @memberof Pet<br>     */<br>    &#39;category&#39;?: Category;<br>    /**<br>     * <br>     * @type {string}<br>     * @memberof Pet<br>     */<br>    &#39;name&#39;: string;<br>    <br>    // more...<br>}</pre><p>Pretty neat! That’s a lot of weight taken away from the developer’s shoulders. And the quality of the output is probably greater than what you would have achieved by hand.</p><p>But the best part is yet to come. If you scroll further down that file, you’ll find fully implemented methods that you can use to call your endpoints:</p><pre> /**<br> * PetApi - object-oriented interface<br> * @export<br> * @class PetApi<br> * @extends {BaseAPI}<br> */<br>export class PetApi extends BaseAPI {<br>    /**<br>     * <br>     * @summary Add a new pet to the store<br>     * @param {Pet} pet Pet object that needs to be added to the store<br>     * @param {*} [options] Override http request option.<br>     * @throws {RequiredError}<br>     * @memberof PetApi<br>     */<br>    public addPet(pet: Pet, options?: RawAxiosRequestConfig) {<br>        return PetApiFp(this.configuration).addPet(pet, options).then((request) =&gt; request(this.axios, this.basePath));<br>    }<br><br>    /**<br>     * <br>     * @summary Deletes a pet<br>     * @param {number} petId Pet id to delete<br>     * @param {string} [apiKey] <br>     * @param {*} [options] Override http request option.<br>     * @throws {RequiredError}<br>     * @memberof PetApi<br>     */<br>    public deletePet(petId: number, apiKey?: string, options?: RawAxiosRequestConfig) {<br>        return PetApiFp(this.configuration).deletePet(petId, apiKey, options).then((request) =&gt; request(this.axios, this.basePath));<br>    }<br><br>    /**<br>     * Multiple status values can be provided with comma separated strings<br>     * @summary Finds Pets by status<br>     * @param {Array&lt;FindPetsByStatusStatusEnum&gt;} status Status values that need to be considered for filter<br>     * @param {*} [options] Override http request option.<br>     * @throws {RequiredError}<br>     * @memberof PetApi<br>     */<br>    public findPetsByStatus(status: Array&lt;FindPetsByStatusStatusEnum&gt;, options?: RawAxiosRequestConfig) {<br>        return PetApiFp(this.configuration).findPetsByStatus(status, options).then((request) =&gt; request(this.axios, this.basePath));<br>    }<br><br>    // more...<br>}</pre><p>Yep. Fully typed APIs. All of this was generated from a single OpenAPI file 🤯</p><p>Would you like to add a Pet to the store? No problem:</p><pre>const added = await new PetApi().addPet({<br>  name: &#39;Pikachu&#39;,<br>  photoUrls: [&#39;https://example.com/pikachu.jpg&#39;],<br>  category: {<br>    id: 123,<br>    name: &#39;Pokemon&#39;<br>  },<br>  tags: [{<br>    id: 123,<br>    name: &#39;Electric&#39;<br>  }],<br>});</pre><p>If you dig into the generated code, you’ll see that it takes care of validating the parameters, setting the right verb and headers, serializing the data if needed, sending the request with Axios, and returning the result, complete with the expected type.</p><p>And in case the API or the server required some configuration, or you wanted to use your own Axios instance for example, the generated code has got you covered too:</p><pre>const api = new PetApi(<br>  new Configuration({ accessToken: &quot;eyJ0eXAiO&quot;, apiKey: &quot;key-abc&quot; }),<br>  &quot;https://api.example.com&quot;,<br>  myAxios<br>);</pre><p>You’re welcome.</p><h3>OpenAPI in the real world</h3><p>The quality of the generated API will depend on the quality of your documentation. The <a href="https://raw.githubusercontent.com/openapitools/openapi-generator/master/modules/openapi-generator/src/test/resources/3_0/petstore.yaml">petstore.yaml</a> file used in this example is good, if not perfect. Yours probably won’t be as good.</p><p>However, the best way to improve your OpenAPI documentation is to start using it to generate code. This will allow you to identify gaps and fix them until you’re happy with the output. By experience, I have seen great improvements to the OpenAPI documentation once developers started using it for code generation, leading to greater adoption (not to say <em>addiction</em>) on both the backend and frontend sides.</p><p>Generated APIs can save you a lot of time. But they also eliminate a whole category of bugs thanks to type safety. When your API eventually changes, TypeScript promptly lets you know what parts of the code need updating.</p><h3>Coming up in part 2</h3><p>In the next part, we’ll see how to integrate a generator into your existing project and make the best use of the CLI options.</p><p>I’ll also show you how to create reusable React hooks to query and cache the data. And if you like it, I might even teach you how to mock the server, so that you can start creating and testing your pages even before the service is implemented 😮</p><p>So when you’re ready, head this way:</p><p><a href="https://medium.com/@gfox1984/fully-typed-web-apps-with-openapi-part-2-6ae1b8c6de92">Fully typed Web Apps with OpenAPI (Part 2)</a></p><h3>Stackademic 🎓</h3><p>Thank you for reading until the end. Before you go:</p><ul><li>Please consider <strong>clapping</strong> and <strong>following</strong> the writer! 👏</li><li>Follow us <a href="https://twitter.com/stackademichq"><strong>X</strong></a> | <a href="https://www.linkedin.com/company/stackademic"><strong>LinkedIn</strong></a> | <a href="https://www.youtube.com/c/stackademic"><strong>YouTube</strong></a> | <a href="https://discord.gg/in-plain-english-709094664682340443"><strong>Discord</strong></a></li><li>Visit our other platforms: <a href="https://plainenglish.io/"><strong>In Plain English</strong></a> | <a href="https://cofeed.app/"><strong>CoFeed</strong></a> | <a href="https://differ.blog/"><strong>Differ</strong></a></li><li>More content at <a href="https://stackademic.com/"><strong>Stackademic.com</strong></a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=595d55766670" width="1" height="1" alt=""><hr><p><a href="https://blog.stackademic.com/fully-typed-web-apps-with-openapi-part-1-595d55766670">Fully typed Web Apps with OpenAPI (Part 1)</a> was originally published in <a href="https://blog.stackademic.com">Stackademic</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>