In the dynamic world of React, ensuring that your code remains clean and maintainable is paramount. Dive into a session that demystifies the intricacies of structuring your React projects, distinctly separating concerns, and adhering to best practices that stand the test of time. Drawing from real-world experiences and hands-on tips, this talk promises to arm you with the knowledge to elevate your React codebase to the zenith of clarity and efficiency.
Crafting Pristine React: Best Practices for Maintainable Code
AI Generated Video Summary
React best practices, including state management, component performance, testing, accessibility, and clean architecture, are discussed. The use of useMemo and useCallback should be limited to when necessary, and tools like React's new compiler and Million.js can aid in performance optimization. End-to-end testing and React Testing Library are important for critical functionalities. Accessibility is emphasized, and the use of the xCore/React package is recommended. Logic business can be extracted from components, and file naming and folder structure should be carefully considered. Import aliases and different folder structures can enhance code maintainability. The talk also touches on managing hooks and testing, and ends with a discussion on favorite pizza and online presence.
1. Introduction to React Best Practices
React provides the fundamental building blocks for creating a splendid application from scratch. Working with React is like making a pizza, incorporating all functionalities, components, dependencies, libraries, and logic. However, software tends to degrade over time. In this talk, I'll share best practices, insights, and common pitfalls related to component performance, state management, testing and accessibility, logic business, and code structure.
Well, thanks. I'm super excited to be here today. I love React Day Berlin. I love Berlin. I love React. And React is awesome. I mean, it provides the fundamental building blocks for creating a splendid application from scratch, allowing you to choose each ingredient step by step. Sometimes working with React, it feels like making a pizza. You have some basic constraints such as its shapes and the traditional method of building it. And by incorporating all your functionalities, components, dependencies, libraries, and logic, you end up with your perfect pizza.
But a few months later, that once glorious and beautiful pizza transforms into this. Yeah. But with React, who has experienced this? Raise your hand. Almost everyone. Yeah. That's typical. And David Lortz once said that programs like people get old. We can't prevent aging. But we could try to limit its effects. So, too long to read the blog quote. Software tends to degrade over time. Within 20 minutes, I'll be sharing best practices, insights, and common pitfalls related to five distinct React topics. First, we will talk about component performance, state management, testing and accessibility, logic business, and finally, files, folders, and code structure.
2. Introduction and State Management
I create content about web development on YouTube and Twitch. I have over 15 years of experience in software development. I've been working with React for seven years now. My most recent role was as frontend lead where I spent five years. I made the leap from my day-to-day job to focus on creating content on Twitch and developing my own products. Best practices from five years ago or today are not the same. Context matters. Factors such as team expertise, organizational structure, libraries in use, and the need for rapid iteration all play a role. I'll skip over some of the more common best practices for React applications. Let's begin with state management. And the most simple use selector to extract a state from the store would be this one. Our component becomes intertwined with the React Redux package.
I create content about web development on YouTube and Twitch. Just in case you want to start learning Spanish, who knows? You can find me in YouTube with midu.live.
A bit about myself. I have over 15 years of experience in software development. I may look young, but I'm approaching 40. I've been working with React for seven years now. Starting with the infamous Create Class. Anyone tried Create Class from here? Yeah. A few of you. Yeah. I'll give you a hug after the talk. Fortunately, past that era.
My most recent role was as frontend lead where I spent five years. We undertook the significant task of migrating a monolithic application to a new React application. A year ago, I made the leap from my day-to-day job to focus on creating content on Twitch and developing my own products.
So, before starting, because I want to acknowledge that discussions about best practices can often ignite passion. I mean, that's a bit cool, but sometimes even a bit of fanaticism. So, best practices from five years ago or today are not the same as maybe they would be in two weeks, one month, or one year later. These practices might evolve over time, or they may not be applicable to your specific use case. Because context matters. Factors such as team expertise, organizational structure, the libraries in use, the need for rapid iteration, even at the cost of technical depth, all play a role. So, even if you disagree with some of these practices, which is completely fine, I encourage you to remain open-minded and make your own.
I'll skip over some of the more common best practices for React applications. Okay? And let's begin with state management. I'm pretty sure that you know this library logo, Redux. It's everywhere. And in every codebase. And the most simple use selector to extract a state from the store would be this one. And we import the hook from Redux and use it to access a specific slice of a state we're interested in. And as we could observe, even with the simplest component, simpler component, our component becomes interwinded with the React Redux package.
3. State Management and Component Performance
This approach is manageable for small projects or maybe medium-sized projects. But could be posed challenges as the project scales up. One piece of advice for best practices on React would be to grab your state library with your own hooks. It's best to encapsulate your state library. Create a custom hook that focuses on the data itself. Don't leak your library across all your components. Avoid this kind of custom hooks of user store, use local storage, use Fetch, and instead focus on your data. And name your custom hooks in order to change the implementation. Now talking about component performance, useMemo and useCallback are designed as performance optimization tools for React applications. But I would say that they should be used only when truly necessary.
This approach is manageable for small projects or maybe medium-sized projects. But could be posed challenges as the project scales up. If you want to change to Sustan, somebody could think about use final replace, but it's not that easy. Because you have a ton of results. And it has some drawbacks. Because you could miss some files. It's a manual process. A lot of file changes. And even maybe it's too complex.
Because some components like this one, it's even for a small component, it's bloated with numerous references to Redux. So, one piece of advice for best practices on React would be to grab your state library with your own hooks. It's best to encapsulate your state library. Because then and this is applicable to any library. Because it ensures a more streamlined and adaptable integration of state management within your application.
So, obviously, you could create a custom hook, and that would be something like that. And the most important thing is not even the custom hook, but the naming of it. Because if you notice the name of the hook, it doesn't specify how or from where we are extracting the data. But rather focus on the data itself. And when we are using it, we are removing any specification of Redux. And even if we check the most complex example in this instance, the improvement is going to be significant. We move from this, that is super bloated with Redux code, to something like this. Now it is understandable. It doesn't care about where I'm getting the data. And it's focusing on a few lines of code.
So, don't leak your library across all your components. Avoid this kind of custom hooks of user store, use local storage, use Fetch, and instead focus on your data. And name your custom hooks in order to change the implementation. And don't even have to go to every one of your components to move from one library to another. Now talking about component performance, useMemo and useCallback are designed as performance optimization tools for React applications. But I would say that they should be used only when truly necessary.
4. Optimizing Performance and Testing
Utilizing useMemo and useCallback excessively can complicate readability and add unnecessary complexity without significant performance improvements. React's new compiler, under development, aims to automatically memorize components and functions. Another option is Million, a virtual DOM replacement for React that offers automatic performance improvements without code changes. Profiling from dev tools is crucial to identify re-rendered components and evaluate their cost. When it comes to testing and accessibility, integration tests are essential.
And I mean, a lot of times on my last company, we had a lot of code like this one. They are utilizing the useMemo hook to prevent recreating posts on every render if the post remains unchanged. And this approach could make sense sometimes for some specific cases. But the problem is that sometimes you'll find something like this inside a component. The issue arises because the same component is using one useCallback and useMemo four times. And this overuse complicates readability, adds unnecessary complexity, and in some instances doesn't result in a tangible performance improvement.
So, what can we do? We could wait for React forget the new compiler that React team is working. It's still under development. It was announced in 2021. And it's designed to automatically memorize our components and functions. Three months ago, they shared a small update that they're still working on it. I don't know if it's going to be launched any day. But another possible solution could be considering using Million. Million is a virtual DOM replacement for React. And you don't need to change or add complexity and lights of code in your codebase. But instead, it offers significant performance improvement automatically. So, it compiles. It acts like a wrapper in some components. And it improves their performance. And you don't have to change anything. You don't have to add useMemo or useCallback. It doesn't do the same. But the performance improvement is considerable.
What I want to share is this kind of phrase that is super important. The primitive optimization is the root of all evil. The piece of advice here is that you have to profile from the dev tools to identify which components are being re-rendered. But not only that, but also check the cost of it. Because sometimes, even if it takes one millisecond or even less, it doesn't make any sense to put more code in order to try to fix something that is not a problem. Talking about testing and accessibility, consider an application using Amazon as an example. If you were limited to performing just one type of testing, which would it be? You need integration tests.
5. End-to-End Testing and React Testing Library
Implementing end-to-end tests in your application is crucial for critical functionalities. Tools like Playwright offer mind-blowing features, such as a Visual Studio Code extension that generates code while you navigate your website. The API is similar to React Testing Library, which is considered the best API for testing React applications.
The obvious choice would be an end-to-end test. If you only have one test in your application, it should be an end-to-end test. And currently, there's no excuse for not implementing some end-to-end test in your application to critical functionalities. Nowadays, these tests are faster than some years ago and they are more affordable. And we have some tools like Playwright that are mind-blowing. For example, they have this Visual Studio Code extension, where you open your website and while you are navigating your website and you are trying your application, then it's writing for you all the code. And even the API is super awesome because it's super similar to React Testing Library that I think and I find that this is the best API for testing for React applications.
6. Accessibility and Reporting
A lot of times people don't care about accessibility. My recommendation would be to install the package xCore/React. It's super easy to understand and configure. You can create a method to report accessibility issues and get detailed information about the issues in the console log.
And also about accessibility, a lot of times people don't care about accessibility. Sometimes it's because we are, I don't know, ignorant, maybe we are lacking information about how to fix those things. My recommendation would be to install this package, xCore slash React. It's super easy to understand. It's straight to the point. And I will show you how to configure it and the information that you are going to grab.
You could create a method to report accessibility issues like this one. The only thing is that it's going to be executed on client and on development mode. And we're using some dynamic import to only import those dependencies for development. So we avoid adding the bundle to the production bundle, these packages. Then in your entry point, you should use this new method, two lines of code, report accessibility, and then pass the React instance really to the method. And that's the information that you are going to get. You are going to get in the console log, you are going to get some issues, serious issues, moderate issues, and not only the typical issues that you could find in the Lighthouse, which are cool, but some more interesting even for the semantic of the HTML, the missing area roles, and much else.
7. Logic Business and Clean Architecture
Normally, if we see a use effect in a component, it could be a smell of something. It's not always, it's not a silver bullet, but it might be an indicator that it could be a refactor into a custom hook. Sometimes custom hooks are not enough, at least for bigger apps, for enterprise apps. So, the idea would be to extract your logic from components. Clean architecture could make sense if you are working in a very big company. It could help test our logic business without the need of thinking about React. The logic business is what is making money for you. You could move your logic business to a different library, even for different devices, applications, and so on.
Talking about logic business, normally, if we see a use effect in a component, it could be a smell of something. It's not always, it's not a silver bullet, but it might be an indicator that it could be a refactor into a custom hook. This is a typical example of usage of use effect, and we could extract it to a custom hook. And even we could go further using use query from React query in order to avoid the need to manually match manage states for loading error and data.
Sometimes clean architecture could make sense if you are working in a very big company. If you are only a team of three, maybe it's not. It could be our engineer. But in a company with hundreds of developers, it could make sense to create value objects and entities based on the domain-specific design. Repository interfaces in order to be able to change from your Apple repository, or maybe local storage repository, or directly to database repository. You could create your own API repository. And even create a service that you could inject if you are using the API repository or a mock repository. This is great. This is helping to test our logic business without the need of thinking about React. The logic business is the most important thing that you are going to have. Because React, I hope that it's going to be alive for a few more years. But the logic business is what is making money for you. And you could move your logic business to a different library, even for different devices, applications, and so on. And have you seen this image on Twitter, right? It was the meme.
8. Server Actions and File Naming
The useServer magic string allows you to extract server actions to a different file, making them invisible to the client bundle. It's crucial to be mindful of file naming and folder structure to avoid subtle bugs, especially on Linux-based systems. Unix systems are case sensitive, while Mac systems are case insensitive, so it's best to use camelCase for file names and folders, even for React components.
And the problem with this kind of image is that it lacks context. This is the possibility that you could do with Next.js now, with React, with the form actions. But the thing is that you are able to extract this to a different file. You are not forced to do this in line inside the button. In this case, you are able to extract all the logic of the server actions. And you could do some validations. You could do everything. And the only important thing is that you use the useServer magic string on the file. And then it becomes invisible to the client bundle.
The last thing is files, folders, and code structure. It's super interesting because there are tons and tons of articles on the internet talking about this. And it's normal because it's a challenge. It's super important about the context of each application. And because everyone has a different opinion of it. But one thing that is, for me, super, super important. If you check this code, it appears to be fine. But there is a subtle bug that could cause your continuous integration system, particularly if it's based on Linux, to fail. And it's super hard to see. And I know, by experience, that it could take hours to get the error.
The problem with this code is this. The thing is that Unix systems are case sensitive, whereas Mac systems, that is the one that we're using, except this one that is using Adele, are case insensitive. So my whole take about this is avoiding camelCase for file names and folders. Prefer camelCase even for React components. And I know that this is not popular. I know it. A lot of people now are taking photos. I know about it. But I thought, like, some years ago, that the perfect case for React components were Pascal case. And after that, I was checking a lot of repositories on GitHub about popular projects like Next.js. And they are using camelCase for all their files, even React components.
9. Import Aliases and Folder Structure
And if you don't like Next.js yourself, because I don't know, Peanuts, even remix in the repository for components, they are using camelCase. And that is not why they invented it. I mean, there's a reason behind this. So, I know that it could be hard. But for me, it's not negotiable.
Another thing is always using import alias to enhance maintainability of the code. Import aliases enable you to avoid using relative paths in your imports. So this kind of code that are super hard to read and understand where they come from, they come from this to this. It's much clearer by utilizing import aliases.
And finally, talking about the structure. This is the classic folder structure. I mean, it's fine. It's good for a lot of things. It's perfect for a small and medium projects. But when things get bigger, it's hard to scale. This is a different kind of folder structure that is super interesting for big and huge projects. And it's that your architecture should inform readers about the system itself, rather than the frameworks and dependencies that you are using. In the classic one, you have even a folder that is called Redux. So, in this case, if you are, I don't know, building a healthcare system, then new programmers view the source repository, and their initial impression should be, oh, this is a healthcare system. Some drawbacks, of course. The learning curve is complicated. The initial overload, the duplication risk, because maybe you duplicate some components. But at the end, for huge projects, it's a must.
Architecture and Managing Hooks
Available in English, Portuguese, and Spanish. But if you ask me to translate it to German, I will do it. One thing which I find interesting about your talk is, you dived into some actual things people can just change in their files, in their code directly. Especially when you talk about architecture, I think about some people work in small to medium businesses, some people work in huge companies with hundreds of developers, and they have sort of different needs. And especially for the kind of small to medium businesses, like is this sometimes architecture? Is it over engineering? Are we putting more effort into building things that are bigger than the requirements we have? We have another question. The top-voted question with a lot of people asking is, how do you manage your hooks? It's very easy to create hundreds of custom hooks when following some of these tips.
Available in English, Portuguese, and Spanish. But if you ask me to translate it to German, I will do it. Well, I'm not doing it. It's a little bit difficult. But thank you.
What I'm gonna do, by the way, is I'm gonna save the fun questions for the end, all right? And we'll start with the technical questions first. But one thing which I find interesting about your talk is, you dived into some actual things people can just change in their files, in their code directly. And then you spoke to some broader kind of architectural ideas and themes. And especially when you talk about architecture, I think about some people work in small to medium businesses, some people work in huge companies with hundreds of developers, and they have sort of different needs. And especially for the kind of small to medium businesses, like is this sometimes architecture? Is it over engineering? Are we putting more effort into building things that are bigger than the requirements we have? Absolutely. I mean, I wanted to share my experience working on a huge project, because I think it's important. And usually we don't talk about it. And we use clean architecture on the front-end side, which is not common. But obviously, I don't recommend people from a small company, three people on the team, 15 people. Maybe it's not worth it. But even that, they could try to separate the logic on one file, agnostic from the framework. And that, for sure, is going to help. It's super easy, cheap. It's like less than one minute. And it could help you to try to test better, share this logic with another components, or maybe even to move to a React native application and use that logic. And it's like one minute of that. And I think that's the good thing about clean architecture, is that it spills out into all the other things. Like your tests will thank you. If you need to go cross-platform, they will thank you and all of those things. So great answer. Great answer.
We have another question. The top-voted question with a lot of people asking is, how do you manage your hooks? It's very easy to create hundreds of custom hooks when following some of these tips. So we're going to have lots of different talks. Lots of different people are going to come up with different hooks that people should use.
Managing Hooks and Million.js
The idea of the scrimming architecture folder structure makes it easier to create and manage hooks. Million.js has some compatibility issues and requires adding an exclude component array. It offers a drop-in replacement for React internals. Test-driven development is useful in some cases but not necessary all the time.
How do you manage all of them? Yeah. Great question. That's why I wanted to introduce the idea of the scrimming architecture folder structure. Because if not, you have a folder called hooks. And you have like 100 hooks inside. Then you have to check like, OK, where is the one that I need? Or I'm going to duplicate some. So the idea with the folder structure of the scrimming architecture is that, as you are managing the hooks components or something else, based on your features or your logic business, it's going to be easier to create and manage those hooks.
I totally understand it. And the next one is, I think I missed this in your code, but it's what is the catch with Million.js? Can you drop in a replacement without any errors? So, yeah, of course, there is a catch. One catch is that it's not 100% compatible. There are some kind of corner cases. For example, you have to add exclude component array in order to avoid to use it everywhere, because it could be problematic. And the other catch is that you are adding a new dependency. Maybe there's some cost in the compilation time, build time. But other than that, the catch is that I love React. But Virtual DOM and even some internals from React, they're a bit old. And it's very easy to create a drop-in replacement better than some internals that we have from React. I love React, but it has some time now.
Absolutely. There are definitely some things that only make sense in the React ecosystem, make no sense anywhere else as well. We've got another question, and this one is about test-driven development. What do you think about it? I love TDD. But for me, there is no silver bullet. I know that there are people that I only develop using test-driven development. Sometimes I use it, especially for logic, because I could create the logic, return the JSON directly, and then go backwards trying to make the test pass and so on. But I don't know. It depends. Some days I want to try TDD and use it. And sometimes I'm more creative, and I try creating the component. So I think that it could help in some cases, but I would like to use it only when it makes sense, not 100% of the time.
Testing, Toolkits, and Pizza
Writing tests has its own ecosystem of bugs and errors that are not your code. Zustand is great for small projects, while Redux Toolkit is better for larger ones. Context API can be used for static states. Each solution has its drawbacks and advantages. Let's move on to some fun questions. Why not paella? Pizza is more universally understood. What's your favorite pizza? As long as it doesn't have pineapple, any pizza is acceptable. My favorite is chicken with chocolate.
Yeah, totally. We were at TestJS Summit yesterday, and it's really interesting how writing tests has its own ecosystem of bugs and errors that are not your code. Your code could be fine, and then the tests have a problem, which is really interesting. And there is no silver bullet. It is the classic it depends answer. It depends, yeah.
For sure, for sure. We have another one. This is more about tools and toolkits. Why Zustand over Redux Toolkit? So it's the same. It depends. I mean, even why not Context API? Context API could make sense for a state that is not going to change often for something that is static. Zustand is great, for example, for small stores, small projects. If it gets bigger, Redux Toolkit, the good thing that you have, it's a great documentation, a great community. Now you have immutability that is great as well. The problem is that it's bigger, but I mean, there are drawbacks and good things of each solution.
No, totally. All right, we have less than a minute left to finish. I won't be able to go through more of the technical questions, but let's go through some of those fun ones. You chose pizza. Why not paella? It's more complicated to share a joke about paella here because maybe people are going to think that paella is great in every photo. It doesn't matter. But in pizza, it's not normally having a chicken. Maybe you get a chicken and maybe people think that's normal. I'm pretty sure there are some Italians who were screaming internally at the screen when they saw that picture. Speaking of that, what's your favorite pizza? Wow, that's a complicated one. Please, just as long as it doesn't have pineapple in it. If there's any right answer, accept that one. No, no. My pizza is chicken with chocolate.
Favorite Pizza and Online Presence
My favorite pizza is sobrassada, which is like butter with honey and cheese. For more information, check out the speaker Q&A and discussion room. You can find me online at Middle-def everywhere.
No, no, no. My favorite pizza is a special one because it's sobrassada. I don't know the word in English, but it's like some kind of pepperoni, but it's like butter with honey and cheese. Awesome.
We've run out of time, but people want to find out more. Someone was asking about the GitHub. They can find you over at the speaker Q&A and discussion room. Awesome. And where can people find you online? Middle-def everywhere. All right, check him out. Give him a round of applause.