Statically Detecting React App Bugs with TypeScript and ESLint

Rate this content

There are amazing tools out there providing you with excellent type safety. But when you get to the client-side fetching, things go wild. Even if you have perfectly typed backed, you lose the type information during the client-side communication. Yes, you can use GraphQL or protobuf and generate types, but... what if I told you there's an easier way? A way that lets you develop your apps smoother than with REST or GraphQL? How? RPC! Say hi to maximum productivity with fantastic developer experience.

21 min
05 Dec, 2022


Sign in or register to post your comment.

AI Generated Video Summary

This Talk explores static analysis tooling for JavaScript and TypeScript, focusing on detecting bugs with TypeScript and ESLint. The importance of type checking and extending recommended rules is emphasized. The Talk also delves into specific ESLint rules and provides code samples to demonstrate their usage. The insights provided by ESLint powered by TypeScript are highlighted. Floating promises and misused promises are discussed, along with recommendations for handling exceptions and resources. The Talk concludes by offering resources and support for further exploration and development.

1. Introduction to Static Analysis

Short description:

I'm Josh Goldberg, an independent open source maintainer focusing on static analysis tooling around JavaScript and TypeScript. Coding can be falsely intimidating due to incorrect assumptions and poor explanations. Many people find it hard to imagine using a computer or learning to code because they grew up thinking computers are difficult. Static analysis tooling is often seen as intimidating but it doesn't have to be. The concepts I will explain today are relevant and applicable for your projects.

Hey, everyone, I'm Josh Goldberg. I'm an independent open source maintainer focusing on static analysis tooling around JavaScript and TypeScript. Most notably, I work on TypeScript ES that allows you to run ESLint on your TypeScript code. I'm also the author of the book recently published through O'Reilly.

And honestly, React Debra Lynn, I've got some anger in me. I've got a lot of problems with the tech industry, and now you're going to hear about it. As software developers, we've mostly become accustomed to the fact that many of our friends and family are never going to understand what we do. We don't like it. We disagree with their unwillingness to learn, but some people just cannot or will not be able to understand computers or even learning to code. Here's an actual quote from my fantastic loving parents, we don't know what TypeScript code is, but we're very proud of Josh and are sure it will be a lovely book. Referring, of course, to Learning TypeScript, available on Amazon O'Reilly and other retailers, buy it today.

Okay. Coding can be falsely intimidating, the issue is not that the concept of using a computer is unusually difficult on its own, many people can figure that out, it's that it is incorrectly assumed to be difficult by many folks. Many people who aren't tech capable find it hard to imagine using a computer at all, even, God forbid, even learning to code, and so are intimidated and distracted and discouraged from doing it. A big source of the problem is that a lot of folks grew up accustomed to the idea that computers are difficult, impossible to understand contraptions. And then they have a bunch of people yelling at them, oh, send a file, open Facebook, attach an attachment, press start, whatever, that's hard. What on earth? And the problem is made worse because many developers, because many people, do not explain technical or complex subjects well. It's something a lot of us struggle with. And a non-technical person can only go through so many bad explanations, convoluted descriptions, before they decide the problem is me, I'm just never going to understand this.

And let that be context for you, as I talk about two problems, changing context and bad explanations, for static analysis, tooling that analyzes your code without running it. Because in parallel, many software developers experience that very same false intimidation, even though they've already learned how to code, they know how to use computers, when they try to think about static analysis. Static analysis tooling for many years was thought by many to be just in the purview of academics or hardcore language engineers. And when they tried to look at resources for static analysis, what they got was high-level theoretical explanations, academic shenanigans, things that are theoretical rather than introductory and practical. But that's not the case. Maybe it never was. It certainly isn't now. Static analysis does not have to be intimidating. The static analysis landscape today, especially for modern TypeScript codebases, I promise you is much more approachable than it's ever been. I think you will find that the concepts I'm going to walk you through today are very relevant and applicable for your projects, and you may even enjoy using them when you get back to work on them. So this is static analysis is awesome, a.k.a.

2. Detecting Bugs with TypeScript and ESLint

Short description:

Combining ESLint and TypeScript allows for detecting application defects and bugs through actual bugs in your code. TypeScript understands the meaning of your code and provides APIs for more informed linting decisions. We'll walk through a component example to explore potential pitfalls and the importance of type checking with TypeScript. Using the TypeScript ESLint plugin, we'll parse our project and make object types available to ESLint rules. Extend the recommended rules from both ESLint core and TypeScript ESLint for better results.

statically detecting React app bugs with TypeScript and ESLint. With me, Josh Goldberg. I'm going to show you how combining ESLint and TypeScript allows for detecting whole swaths of application defects and bugs, such as unused code or unnecessarily async code, through actual bugs in your code, that you will find before your app mysteriously breaks only for some users some of the time.

Let's do this. First, some context setting for the tools themselves, TypeScript and ESLint. Why would you want to level up ESLint rules with TypeScript in the first place? ESLint sees only the raw syntax of your code. It can check for rudimentary issues, such as unused variable, naming convention violations, and so on, but it doesn't really understand what that code means. TypeScript understands what that code means. It does a deep dive into your code, it understands your types, it understands what everything is meant to be, and then has APIs that we can use in our lint rules to make much more informed decisions based on those types.

Today I'm going to walk you through some components that look a lot like this my button example, and we're going to play one of my favorite programming games, Is This Safe? This component is meant to take in give or take two props, I've hid anything unrelated. Action and children, this action in particular is relevant and interesting, it's supposed to be a function call between two console logs. At first glance this code seems safe, it's fine, we take in two props, we render children, we pass this on click call back to a button, it's all fine. But without TypeScript's deep knowledge of the type system and this code, we have no way of linting about a whole bunch of plethora of bad potential pitfalls. So we're confused. Maybe async actions aren't allowed in this app. Maybe action isn't always provided. If it's a function in the first place, which we don't know, maybe it's supposed to take args. Maybe we actually want to explicitly handle rejected promises. There's all sorts of stuff we can't know without understanding what the actual type of our object is. What the types in our code are. So now we're going to go through some type checking. We're going to use TypeScript to parse our full project to understand potential object types. And then make those types available to ESLint rules using the TypeScript ESLint plugin. Now, if you have set up ESLint on TypeScript code before, it's likely you've done something like this. You've installed the TypeScript ESLint parser package, which is the parser that allows ESLint and fun fact, also Prettier, to read in TypeScript syntax from your files. And maybe you've also installed TypeScript ESLint ESLint plugin. The package that contains a whole bunch of Lint rules tailored to TypeScript code. You may have been set in your ESLint config, the parser and plugin settings. If this is all new to you, fun fact, that's how you do it. But there's another step I recommend many codebases take, which is extend the recommended rules from both ESLint core and TypeScript ESLint.

3. Exploring TypeScript ESLint Rules and Code Sample

Short description:

For most projects, it's recommended to use the standard rules from both ESLint core and TypeScript ESLint. However, the recommended TypeScript ESLint rules do not include rules that require type information, as they can slow down the linting process. To enable these rules, you can extend the recommended rule set, configure the parser options, and specify your tsconfig file. Now, let's move on to the code. We'll be looking at a demo application called TypeScript ESLint React demo, which is a simple create-next-app application. It has a page with buttons that increase a call count and change the page background color. The first rule we'll explore is awaitAvailable, which checks if the await keyword is used on a value that is not available. Now, let's take a look at the code sample in our homepage, where we have an example action function that sets the color state and increments the call count. This function is used by our await thenable components.

For most projects, you generally want the standard good recommended rules from both of them. ESLint core enables stuff in all JavaScript, whereas TypeScript ESLint rules are tailored to TypeScript code, and these are great pieces of information. recommend using them. But, one caveat, we don't include rules that require type information in the recommended TypeScript ESLint rule sets, because asking TypeScript for type information does make your lint take a little longer, and requires a little bit extra config, so if you want to enable those rules, you can extend the recommended requiring type checking rule set, config, and also add in a parser options dot project setting to your config, pointing to your tsconfig. This allows our plugin rules to know that's the tsconfig to use for type information to know your TypeScript compiler options.

All right, we're done. Four and a half project settings in, and we are ready to look at some code. The demo we're going to go through today is open source. You don't have to clone it to attend this talk, but if you want to play along, you can. slash Josh Roke-Goldberg slash TypeScript ESlint React demo. It's also Love my Vercel employees. And it is a rather rudimentary create next app application. It's just got one page. We've got a call count state and a custom color state, defaulting to zero and undefined, nothing, respectively. On this page, we have some buttons. When I click a button, I increase the call counts and set a custom color to a random color, which then also changes the page background. Great.

The first rule I'm going to look through is awaitAvailable. From rules slash awaitAvailable, the docs page for this rule. If the await key word is used on a value that is not Available, the value is directly resolved immediately. While doing so is valid JavaScript, it is often a programmer error. Fun fact, Available are most commonly used as promises. Available is a description of the type of promises the actual class. I'm going to show you the code sample here. In our, whoops, got the extra demo there. There we go. In our homepage, pardon me, we have this function here, this example action, which creates a color variable equal to a random color, increments a call count state that's set up here by one, sets the color as the custom color state and returns that color. This function is then used by our await thenable components. Now await thenable takes in action in children, much like that, my button example, and it sets a piece of state running to true or false before and after action.

4. ESLint and TypeScript Code Insights

Short description:

The button should be disabled while the action is running, but it's not. The ESLint rule complains about an unexpected await of a non-promise non-ventable value. Removing the awaits doesn't affect the code, and we can remove the unnecessary state and simplify the code to one line. ESLint powered by TypeScript provides deep insights into code defects.

And it's got this little button disabled equals running referring to that piece of state so that the button should be disabled while the action is running. For async code, this is totally reasonable, but if we go back to the demo, we never see that the button is disabled. So this may or may not be considered a bug. Some would just call it dead code, but either way, it's a problem that we should fix in our app.

And actually, we're getting a complaint from our ESLint rule here. We've got ESLint extension running in VS Code. Unexpected await of a non-promise non-ventable value. It's complaining that my action is typed as a function that returns void, meaning we ignore what it returns. We don't care about it. But I'm awaiting it, which is wrong, because it's not a promise. It's not available. So if I were to remove the awaits, the code should still run, and if I go to the demo, refresh even. Yep, still looks good. Because that await did nothing.

In fact, if I actually use the little extension popover thingie here, I can quick fix, that the rule tells us, to remove the awaits. And then async our function on click has no await expression gets complained to me from a separate ESLint rule, powered by TypeScript, our require rate rule, letting me know that this function doesn't even need to be async in the first place, because there's no await call on it. And I can even take this one large step further by noticing that I am setting state to true, calling a function synchronously, and then setting the state to false. So this state isn't even necessary in the first place. I can totally get rid of my use states. It's always going to be false, which means the button disabled equals false doesn't even need to be there. My on click is a function that just calls action. So I can just simplify the code to be one line inside the function. Boom. Amazing. Love it. Just to be sure, going back to the demo. Yep, everything here looks good. Awesome. That is one example of three where ESLint powered by TypeScript can inform us of some surprisingly deep insights into our code about code defects. Yay.

5. Exploring Floating Promises and Misused Promises

Short description:

The next rule I want to look at is no floating promises. A floating promise is one that is created without any code set up to handle any errors. Floating promises can cause issues such as improperly synchronized operations and ignored promise rejections. In the demo, we have a button that is supposed to wait before performing an action, but it doesn't get disabled when clicked multiple times. This is because we forgot to add the 'await' keyword before the action. By enabling the 'no floating promises' rule, we get a complaint that promises must be awaited. Adding the 'await' keyword fixes the issue. The last rule I want to show is 'no misused promises', which forbids providing promises to logical locations where they are not handled properly. In the demo, we pass an async function to a button's 'onClick' attribute, causing a complaint because React doesn't handle async functions. By intentionally throwing an error in the async function, we demonstrate that the error cannot be handled by the framework.

But let's move on. The next rule I want to look at is no floating promises. My personal favorite ESLint rule. From the docs page, you will notice this is a pattern. All of our rules are under TypeScript. A floating promise is one that is created without any code set up to handle any errors in my throw. Floating promises can cause several issues such as improperly synchronized operations, ignored promise rejections, and more.

We will see both of those things in this demo. First, I want to show off a direct violation here this button which is slightly more advanced than the previous. It is supposed to wait a second before doing things. And although it works fine if we just click it once, if we click it a bunch the demo is not broken the way it's supposed to be. If we click it a bunch of times we see that it doesn't actually get disabled. What's happening here? Well, we in this no floating promises version call to this example action async which waits a second before calling that same example action and we forgot to add the await in front of the action. Notice how in this one it's typed as a function that returns promise void. Pardon me. So, our code doesn't actually await the function before then setting the states. So, we get this nice little disco effect if we click it a bunch of times. And if we enable the no floating promises rule I just disable that in the config so that we wouldn't get a squiggly immediately in the other files. We get this nice little complaint promises must be list of things you could do such as awaited you must await them. So, I'm going to add in an await here which means now we do await the action before set running false is called and yep our code is fixed as it was before. Yay!

Now, no floating promises is honestly my favorite rule because it can catch a lot of really intense stuff but it's very similar in theory to the last of the three rules I want to show you no misused promises. This rule forbids providing promises to logical locations such as if statements in places where the TypeScript compiler allows them but they are not handled properly. Floating promises are those that are created and then not handled at all, nothing uses them. Misused promises are when you do handle a promise technically you provide it to somewhere but that somewhere doesn't have any logic for handling promises. And to illustrate the difference I will show you the last demo which is our no misused promises component. Here, we are passing this on click to the button, same as we were before. And notice how we got the little squiggly here, the complain, promise returning function provided to attribute or avoid return was expected, because the on click and on whatever events, on DOM elements in frameworks such as React don't handle async functions. They don't handle the idea that the function might take more than constant synchronous time to run. And in this function, I've done something evil. I said that if call count is greater than two and math.random is greater than .5, we're going to throw an error, gotcha, which means that because this asynchronous code is rejecting, we're not going to be able to handle it in our framework.

6. Handling Exceptions and Resources

Short description:

If you encounter a complaint or failure in your code, wrapping everything in a try-catch block may seem like a reasonable solution. However, there are potential exceptions that can still occur, even with try-catch. To safely handle async functions, it is recommended to create a unified handle using a function like 'runSafely', which takes in an action, a function that creates a promise, and a synchronous onError handler. By implementing this approach, you can ensure your application remains safe and doesn't crash, even when users explore edge cases. These are three fantastic ESLint rules from the TypeScript ESLint plug-in that I highly recommend enabling. For more resources, offers great documentation and a user-friendly UI. You can also check out the TypeScript ESLint React demo for a demonstration of these concepts. If you would like to support my work, you can buy my book 'Learning TypeScript' or sponsor me on GitHub. Additionally, the team behind TypeScript ESLint has an open collective and would greatly appreciate your support. Thank you for watching and enjoy the rest of the conference!

If we get a complaint, if we have a failure, haha, there we go. It's just going to go to the root on error listener. Our code isn't going to know what to do with it. Fun fact. This is a very similar popup style in a lot of modern dev tooling such as create react app and create next app where you get this, I think informed by web pack, little complaint.

Now, the way to do this you might at first have an instinct 4 would be to wrap everything in a try catch which is totally reasonable. Oh, no, error. And then maybe also running false there. Which would fix the error if we refresh, go a couple more times and then finally get the complaints. Come on, randomization. You're killing me here. Come on. Oh, lordy. Well, great. Looks like it never broke. I bet if we check the console, yup, got a few oh, no's. But this actually still might be a problem because what happens if you do something dangerous in your catch block, which accidentally has a bug, lol. And then you run it again, and you again are in the case where even though you try catch, you still have a potential exception in your code. So, the actual only way to really safely do this is to make some sort of unified handle for your async functions. I would highly recommend if your application is meant to be strongly safe and that it doesn't crash, even for users who are exploring edge cases, if you really care about your users not experiencing crashes, do some sort of function like this run safely function, which takes in an action, a function that creates a promise, and a synchronous on error, then calls action.catch on error, so that you can create onClicks or other event handlers that are a run safely of whatever your async code is and then something that handles the error. In this case, just for demo purposes, I've set up an error state, which gets shown to the user in a span with ARIA live assertive. So now, if we go to our demo and click, this may fail safely. If it fails, we do still get the gotcha, and then the button becomes undisabled or reenabled, even though the code had a failure. Awesome.

So, those are three fantastic ESLint rules from the TypeScript ESLint plug-in I would highly recommend enabling. And that's all the demo stuff I wanted to show you. For some resources, is fantastic, they just did a big site overhaul with great documentation rewrites and lovely new UI. We had a slightly less impressive site redesign over at TypeScript. And if you want to see the demo app, again, it's linked on the side, TypeScript ESLint React demo. If you want to support me in doing work on making all this better for you, you can buy my book Learning Typescript. And please sponsor me on GitHub, And if you want to support us as the team behind TypeScript ESLint, we do have an open collective, We could really use your support because we're very underfunded and very understaffed right now, and we have a lot of features, performance issues, and bugs that we'd like to tackle this coming year. All that being said, thank you very much for watching. Thank you to the conference organizers for setting all this up. You can reach me on Twitter, Joshua K. Goldberg, and I hope you enjoy the rest of the conference.

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

React Advanced Conference 2022React Advanced Conference 2022
25 min
A Guide to React Rendering Behavior
React is a library for "rendering" UI from components, but many users find themselves confused about how React rendering actually works. What do terms like "rendering", "reconciliation", "Fibers", and "committing" actually mean? When do renders happen? How does Context affect rendering, and how do libraries like Redux cause updates? In this talk, we'll clear up the confusion and provide a solid foundation for understanding when, why, and how React renders. We'll look at: - What "rendering" actually is - How React queues renders and the standard rendering behavior - How keys and component types are used in rendering - Techniques for optimizing render performance - How context usage affects rendering behavior| - How external libraries tie into React rendering
React Summit Remote Edition 2021React Summit Remote Edition 2021
33 min
Building Better Websites with Remix
Remix is a new web framework from the creators of React Router that helps you build better, faster websites through a solid understanding of web fundamentals. Remix takes care of the heavy lifting like server rendering, code splitting, prefetching, and navigation and leaves you with the fun part: building something awesome!
React Advanced Conference 2022React Advanced Conference 2022
30 min
Using useEffect Effectively
Can useEffect affect your codebase negatively? From fetching data to fighting with imperative APIs, side effects are one of the biggest sources of frustration in web app development. And let’s be honest, putting everything in useEffect hooks doesn’t help much. In this talk, we'll demystify the useEffect hook and get a better understanding of when (and when not) to use it, as well as discover how declarative effects can make effect management more maintainable in even the most complex React apps.
React Summit 2022React Summit 2022
20 min
Routing in React 18 and Beyond
Concurrent React and Server Components are changing the way we think about routing, rendering, and fetching in web applications. Next.js recently shared part of its vision to help developers adopt these new React features and take advantage of the benefits they unlock.
In this talk, we’ll explore the past, present and future of routing in front-end applications and discuss how new features in React and Next.js can help us architect more performant and feature-rich applications.
React Advanced Conference 2021React Advanced Conference 2021
27 min
(Easier) Interactive Data Visualization in React
If you’re building a dashboard, analytics platform, or any web app where you need to give your users insight into their data, you need beautiful, custom, interactive data visualizations in your React app. But building visualizations hand with a low-level library like D3 can be a huge headache, involving lots of wheel-reinventing. In this talk, we’ll see how data viz development can get so much easier thanks to tools like Plot, a high-level dataviz library for quick
easy charting, and Observable, a reactive dataviz prototyping environment, both from the creator of D3. Through live coding examples we’ll explore how React refs let us delegate DOM manipulation for our data visualizations, and how Observable’s embedding functionality lets us easily repurpose community-built visualizations for our own data
use cases. By the end of this talk we’ll know how to get a beautiful, customized, interactive data visualization into our apps with a fraction of the time

React Summit 2023React Summit 2023
23 min
React Concurrency, Explained
React 18! Concurrent features! You might’ve already tried the new APIs like useTransition, or you might’ve just heard of them. But do you know how React 18 achieves the performance wins it brings with itself? In this talk, let’s peek under the hood of React 18’s performance features: - How React 18 lowers the time your page stays frozen (aka TBT) - What exactly happens in the main thread when you run useTransition() - What’s the catch with the improvements (there’s no free cake!), and why Vue.js and Preact straight refused to ship anything similar

Workshops on related topic

React Summit 2023React Summit 2023
170 min
React Performance Debugging Masterclass
Featured WorkshopFree
Ivan’s first attempts at performance debugging were chaotic. He would see a slow interaction, try a random optimization, see that it didn't help, and keep trying other optimizations until he found the right one (or gave up).
Back then, Ivan didn’t know how to use performance devtools well. He would do a recording in Chrome DevTools or React Profiler, poke around it, try clicking random things, and then close it in frustration a few minutes later. Now, Ivan knows exactly where and what to look for. And in this workshop, Ivan will teach you that too.
Here’s how this is going to work. We’ll take a slow app → debug it (using tools like Chrome DevTools, React Profiler, and why-did-you-render) → pinpoint the bottleneck → and then repeat, several times more. We won’t talk about the solutions (in 90% of the cases, it’s just the ol’ regular useMemo() or memo()). But we’ll talk about everything that comes before – and learn how to analyze any React performance problem, step by step.
(Note: This workshop is best suited for engineers who are already familiar with how useMemo() and memo() work – but want to get better at using the performance tools around React. Also, we’ll be covering interaction performance, not load speed, so you won’t hear a word about Lighthouse 🤐)
React Advanced Conference 2021React Advanced Conference 2021
132 min
Concurrent Rendering Adventures in React 18
Featured WorkshopFree
With the release of React 18 we finally get the long awaited concurrent rendering. But how is that going to affect your application? What are the benefits of concurrent rendering in React? What do you need to do to switch to concurrent rendering when you upgrade to React 18? And what if you don’t want or can’t use concurrent rendering yet?
There are some behavior changes you need to be aware of! In this workshop we will cover all of those subjects and more.
Join me with your laptop in this interactive workshop. You will see how easy it is to switch to concurrent rendering in your React application. You will learn all about concurrent rendering, SuspenseList, the startTransition API and more.
React Summit Remote Edition 2021React Summit Remote Edition 2021
177 min
React Hooks Tips Only the Pros Know
Featured Workshop
The addition of the hooks API to React was quite a major change. Before hooks most components had to be class based. Now, with hooks, these are often much simpler functional components. Hooks can be really simple to use. Almost deceptively simple. Because there are still plenty of ways you can mess up with hooks. And it often turns out there are many ways where you can improve your components a better understanding of how each React hook can be used.
You will learn all about the pros and cons of the various hooks. You will learn when to use useState() versus useReducer(). We will look at using useContext() efficiently. You will see when to use useLayoutEffect() and when useEffect() is better.

React Advanced Conference 2021React Advanced Conference 2021
174 min
React, TypeScript, and TDD
Featured WorkshopFree
ReactJS is wildly popular and thus wildly supported. TypeScript is increasingly popular, and thus increasingly supported.
The two together? Not as much. Given that they both change quickly, it's hard to find accurate learning materials.
React+TypeScript, with JetBrains IDEs? That three-part combination is the topic of this series. We'll show a little about a lot. Meaning, the key steps to getting productive, in the IDE, for React projects using TypeScript. Along the way we'll show test-driven development and emphasize tips-and-tricks in the IDE.

React Advanced Conference 2021React Advanced Conference 2021
145 min
Web3 Workshop - Building Your First Dapp
Featured WorkshopFree
In this workshop, you'll learn how to build your first full stack dapp on the Ethereum blockchain, reading and writing data to the network, and connecting a front end application to the contract you've deployed. By the end of the workshop, you'll understand how to set up a full stack development environment, run a local node, and interact with any smart contract using React, HardHat, and Ethers.js.

React Summit 2023React Summit 2023
151 min
Designing Effective Tests With React Testing Library
Featured Workshop
React Testing Library is a great framework for React component tests because there are a lot of questions it answers for you, so you don’t need to worry about those questions. But that doesn’t mean testing is easy. There are still a lot of questions you have to figure out for yourself: How many component tests should you write vs end-to-end tests or lower-level unit tests? How can you test a certain line of code that is tricky to test? And what in the world are you supposed to do about that persistent act() warning?
In this three-hour workshop we’ll introduce React Testing Library along with a mental model for how to think about designing your component tests. This mental model will help you see how to test each bit of logic, whether or not to mock dependencies, and will help improve the design of your components. You’ll walk away with the tools, techniques, and principles you need to implement low-cost, high-value component tests.
Table of contents
- The different kinds of React application tests, and where component tests fit in
- A mental model for thinking about the inputs and outputs of the components you test
- Options for selecting DOM elements to verify and interact with them
- The value of mocks and why they shouldn’t be avoided
- The challenges with asynchrony in RTL tests and how to handle them
- Familiarity with building applications with React
- Basic experience writing automated tests with Jest or another unit testing framework
- You do not need any experience with React Testing Library
- Machine setup: Node LTS, Yarn