The Art of ‘Humble Views’: Testing React Native Apps the Smart Way

Rate this content
Bookmark

In this talk, we explore the divisive world of testing, where developers often find themselves torn between writing no tests and striving for 100% test coverage. Learn how to navigate these polarizing positions and adopt a more nuanced strategy that makes testing efficient and effective.We'll dive into the concept of 'Humble Views,' where we minimize untestable objects by extracting logic from UI elements into test-friendly parts of the codebase. This approach simplifies testing, focusing on business logic instead of UI complexities. Discover how the Model-View-Presenter (MVP) architecture helps achieve this, with presenters serving as a logical layer for testing and hooks aiding in separating logic from UI components.Throughout the talk, we'll discuss the trade-offs of this approach, the role of End-to-End (E2E) tests, and how to strike the perfect balance between too much and too little testing. Join us as we delve into the art of creating 'Humble Views,' ensuring that our React Native apps are scalable, maintainable, and effectively tested!

32 min
07 Dec, 2023

Comments

Sign in or register to post your comment.

AI Generated Video Summary

This Talk discusses the challenges of testing in React and React Native applications, particularly with regards to barcode scanning. It explores the violation of separation of concerns in React and proposes the use of the HumbleObject model to simplify testing and improve code cleanliness. The MVP model is also introduced as a way to separate UI state and logic from the component. The importance of following patterns, standardization, and teaching principles is emphasized, along with the benefits of using custom hooks to share business logic. The potential of AI tools in code refactoring is mentioned as well.

1. Introduction to Humble Views and Testing

Short description:

Hello, everyone. Thank you so much, Nathaniel. That was honestly a very kind introduction. Today I'm excited to be in Berlin talking about humble views in React and React Native applications. I head the mobile team in the UK for Theodo, a global consultancy with expertise in React and React Native. Testing is a contentious topic with different viewpoints, from aiming for 100% test coverage to not testing at all.

♪♪♪ Hello, everyone. Thank you so much, Nathaniel. That was honestly a very, very kind introduction. I feel humbled. Come on. All right, dad jokes don't land very well here. Good to know.

All right, okay. So today I'm very excited to be in Berlin with you all. It's my first time in the city, and it is so aesthetically pleasing. The vibes are honestly immaculate, especially with the snow. I'm very excited to be talking about this concept of humble views. We're going to be looking at how you can architect your React and React Native applications to hopefully make it a bit easier to test and also make your applications more scalable.

A little bit about me. Like Nathaniel mentioned, my name is Mo. I head the mobile team in the UK for Theodo. So Theodo is a global consultancy with over 700 digital experts, 150 of which do React Native slash React. So we've been doing React and React Native from the very early days of when they came out. And as a result, we've kind of gone through phases of evolving and seeing different code bases and seeing different patterns and trying to find ones that last a long time and are scalable and hopefully we can share some of that with you today.

So testing is a contentious topic. People typically fall into two camps. If you go down the software crafts person route, you get the likes of Bob Martin, who deeply believed that you need to be aiming for 100% test coverage, that high test coverage is the cornerstone of a good quality code base. So this is the traditional view, and this is the view that the software crafts person will tell you. More recently, on the other side, though, you've got YouTube influencers in the coding space who will tell you, you know what? I've not been testing for years and I've been so fast, you can never catch up with me. I worked at Twitch, trust me, bro. And so you get these like different viewpoints and you get people that are vehemently anti-testing and you get people that are very, very protesting. And so, as with many things in life these days, things get polarized to the very, very extreme. On the left, you've got the people that say, don't test. And if you test, don't ever talk to me again. And then on the right, you've got people saying, your code is bad, you should be testing every single bit of it.

2. Challenges with Barcode Scanner Testing

Short description:

You've got this testing no man's land in the middle where all nuance is lost. I'm aiming to annoy both parties with one talk. Let me set the scene: a React Native project in the fashion industry. A developer wanted to test the barcode scanner but faced challenges with setup. Multiple contexts and mocks were required for testing.

And really, what happens is you've got this testing no man's land in the middle where all nuance is lost. And they sometimes throw tomatoes at each other from either side. But really, you don't get much nuanced conversations around a pragmatic testing approach. And I fancy myself a bit of a centrist. I don't stand for anything. So I'm aiming to annoy both parties with one talk. We'll see how this goes.

Let me set the scene a little bit. So a couple years ago, I was working on a React Native project in the fashion industry. The app was going to be used in warehouses where they were going to scan items of clothing with a barcode scanner inside of the app. And it would store some information. They could enter in some information about the article of clothing. And it was being used by the back office, effectively. There was a developer on my team who was working a lot on the front end stuff. And she came to me at one point and was like, I'm trying to write a test for the barcode scanner. Basically, what I want to test is every single barcode has a checksum. The last few digits will be a sum of some of the values in the middle in some sort of mathematical formula. So I want to test that. But the challenge that she found was that she was needing to do so much setup before she could actually test that by rendering the screen. And what do I mean by that? I've actually went and extracted the code and you'll see how horrifying it is in just about a second. But this screen initially had a bunch of different contexts. And we didn't really need them for the screen itself. But it needed to be mocked so that they could go ahead and test it. Cool. So that's a bunch of mocks. Then some of the clients needed to be mocked for the fetch requests. Expo's barcode scanner, which was an external library that uses the native device, needed to be mocked as well because you don't have access to that in unit tests. Your navigation framework on the native layer needed to be mocked. All right. That's more mocks.

3. React's Violation of Separation of Concerns

Short description:

Afterwards, we wanted to spy on alert, which is the native alerting functionality within React Native. And so she asked me a very good question, which was, does it always have to be so difficult? And it got me thinking about the overall architecture of our code, right? Architecture are the decisions that you wish you could get right early in a project. One of the fundamental concepts that you'll learn within software architecture is this idea of separation of concerns, right? Now we're in React world, and React is a little bit at odds with separation of concerns. React's fundamental selling point and premise of JSX is actually premised on violating separation of concerns. It's this idea that you can bring in your markup into your interactivity layer, start writing HTML-like syntax to represent your UI within JavaScript. So we're bringing all these things together. And this is great, by the way. I'm not negating the fact that this is actually a great addition to have as a framework, because it makes things so much quicker and so much easier to understand with this component-based mindset. What it means is you can put everything in one place and it all just works. So I have the most basic counter example that you can think of. It's in React Native, but it's the same with React. You have at the very top your interactivity. But at the bottom you've got your presentation layer, so you've got your markup and your styling. And if you add a styling framework like Tailwind into it, you've basically got your styling, your markup, so your UI structure as well as your interactivity all in one single file. So separation of concerns becomes a bit more muddled there.

Afterwards, we wanted to spy on alert, which is the native alerting functionality within React Native. Another bit of functionality. And then it just kept going on and on and on. It's just like so many mocks before you actually test what you fundamentally wanted to test, which is the core business logic, right? It's really not a good state of the art when you're testing like this.

And so she asked me a very good question, which was, does it always have to be so difficult? And it got me thinking about the overall architecture of our code, right? And it leads to a very good question, which is like, what is code architecture? And I really like this definition. So architecture are the decisions that you wish you could get right early in a project. It's a quote from Ralph Johnson, who's a computer science professor in the US. He's written several books around good software architecture.

And one of the fundamental concepts that you'll learn within software architecture is this idea of separation of concerns, right? So this idea with separation of concerns is that you can take different components within your application and you try to break them up into individual modules or chunks that are each individually responsible for a single distinct job. And they can do that independent of all the other bits. That's the theory. So the examples that you get is like the split of HTML, CSS, JavaScript in the traditional web, or the IP stack, which is in almost every single computer science textbook, which is like, you've got all these layers to the network stack, and then you can take one out and replace it with something else. And in theory, the rest of the system should work fine.

Now we're in React world, and React is a little bit at odds with separation of concerns. What do I mean by that? So React's fundamental selling point and premise of JSX is actually premised on violating separation of concerns. It's this idea that you can bring in your markup into your interactivity layer, start writing HTML-like syntax to represent your UI within JavaScript. So it violates that old traditional mindset of separation of concerns, that your HTML does your markup, your JavaScript does your interactivity, and your CSS does your styling. And the rest of the ecosystem follows suit. You have libraries like style components, which lets you do your styling within your JavaScript as well, this whole concept of CSS and JS. So we're bringing all these things together. And this is great, by the way. I'm not negating the fact that this is actually a great addition to have as a framework, because it makes things so much quicker and so much easier to understand with this component-based mindset. What it means is you can put everything in one place and it all just works. So I have the most basic counter example that you can think of. It's in React Native, but it's the same with React. You have at the very top your interactivity. So it's the used states that you have, the callbacks that you have to increment the counter. But at the bottom you've got your presentation layer, so you've got your markup and your styling. And if you add a styling framework like Tailwind into it, you've basically got your styling, your markup, so your UI structure as well as your interactivity all in one single file. So separation of concerns becomes a bit more muddled there.

4. React's Challenges with Large Components

Short description:

React doesn't stop you from creating large components that are not modularized correctly. Massive React files with hundreds or thousands of lines become hellish to test. Other architectural patterns like Model-View-Controller and HumbleObject can address these problems. Let's focus on the HumbleObject model. A typical React component can have infrastructure code, business logic, UI state and logic, and JSX with styling.

Take it... You're not meant to read this code, by the way. Don't try. There's no point. But the idea here is if you add a little bit more functionality and keep it within the same component, just by fetching a name of an article and having the button refetch, these files become larger and larger and larger. As you start to make these components more and more complex, the testing also becomes harder because you're adding all of these different dependencies into a single component or a single screen. And this is by far the biggest challenge that people face, I think, when they're designing their architecture. And it's that React doesn't actually stop you from creating these large components that are not modularized correctly. You end up sort of coding yourself into a corner if you're not consciously thinking about how you split your component boundaries within React.

So let's burn this up and look at where this actually goes. A usual symptom that I've seen in codebases, and we've been going on to a lot of different brownfield codebases that you see out in the wild, is that you have these massive React files with hundreds or in some extreme cases even thousands of lines. And in most cases, these components end up being hellish to test as well. So this isn't a news problem. People have been building GUIs since the 1980s, right? How did people deal with this when testing was a challenge? If we go back to the drawing board and we take a step out of the JavaScript world, there's been a lot of work that's been done by people in other languages and other frameworks to look at the architectural space and address some of these problems. So you might have heard of terms like Model-View-Controller, you might have heard of the term called HumbleObject, which we're going to dig into a bit more today. The Model-View-Presenter, it's very close cousin, the Model-View-ViewModel, all these great names, right? You'll love this one. Look at that! The View-Interactor-Presenter-Entity, it's very catchy. Viper, for short. Now we're not going to go into all of these, we're going to just dig into two architectural patterns that I think are actually very helpful in the React space. Let's start with the HumbleObject model.

So let's say we have a component and you want to actually test this component. A typical React component can have many things within it. On one level, you can have infrastructure code. What do we mean by that? It's fetching stuff. It's external services. Maybe you're calling a Sentry, maybe you're calling your Google Analytics, they can all live within a component and sometimes you'll find that they do in the wild. You can have business logic, things like Transformers, rules that you may have in your code, different flows of logic. You can have UI state and logic that is inherently related to rendering. So that's your form fields, your validation rules, so on and so forth, maybe some interactivity that goes into play. And lastly, you have the actual JSX and the styling.

5. The HumbleObject Model and App Setup

Short description:

The HumbleObject model separates the business logic from the component, making testing simpler and less painful. A coding example demonstrates the before and after. The app setup includes a checkout page with various logic, such as discounts based on total, changing shipping costs, and estimated delivery date based on the basket size.

The HumbleObject model is trying to aid with this problem that you may have, which is when you try to test this component in its entirety, it's actually quite complex. Because you need to mock a bunch of stuff, mock your external dependencies, and maybe you don't want to actually test all of this. If we think about what we really care about testing as the base level and the fundamental level, we really want to test the business logic more than anything else. In the case of our barcode scanner, what we care about testing is making sure that the checksum function works well. That if there's a duplicate, and two barcodes match each other, that we throw an exception and we throw a validation rule.

So what the HumbleObject model says is, take that out of the component and separate it into its own module or file or whatever have you. And put your business logic, put the important bits of your logic inside of that. And instead of testing the actual component directly, you can instead test the HumbleObject. And when you test that, it's a lot simpler and there's a lot less pain associated to it. So let's go ahead and look at a coding example for this. And see a before and after.

Cool. All right. So we've got a very basic app set up here. It's a meal prep prep delivery checkout page. So it's not very well designed, but what you basically have is, you have a checkout page and it has some logic that's added into it. So you have some level of discounts. If your total ends up being more than $50, you get a discount. So you can see once I drop below $50 and I'm on $37, the discounts disappear. And you can fetch and add more items if you're still hungry and want more items added. Other bits of logic that are interesting here is your shipping costs will go up and down depending on your total. So if you're getting less than $20, I think you end up incurring a shipping cost of $5. And you'll also see that the estimated delivery date changes depending on how large your basket is. We've got a bit of front end logic that's living here. Let's go take a look at the code. So if we start here, it's a very basic set up. Inside app.jsx, you can see a component that's not even that long compared to what you might see in a while. It's about 110 lines. Adds up if you regularly face components that are around 100 lines long in React. 200 lines.

6. Testing Core Business Logic with a Humble Object

Short description:

Okay, we've got a winner. All right. 600 is the benchmark. You've got an isTomorrow function, a function to get the delivery date, and a generate delivery label. Underneath, there's the shopping cart component with state, effects, and a reducer for the total. There are functions for handling item removal and addition. At the bottom, there's the rendering logic with JSX. Testing this core business logic requires mocking fetch and checking if a discount is applied. A humble object can simplify the testing and improve code cleanliness.

Okay, we've got a winner. All right. 600 is the benchmark. Cool. So there's a lot that's going on here. We're going to quickly run through them. You've got an isTomorrow function. These are some helper functions, their utils. You've got a function to get the delivery date depending on the number of cart items. You've got a generate delivery label. So what this does is it checks is the date tomorrow? If it is tomorrow, just say tomorrow. Otherwise, what I want you to do is render the date in a very specific format. And then you've got the actual React component underneath it. You've got shopping cart, which keeps in state the cart items. You've got to use effects to fetch the starting basket. And then afterwards, you have a reducer to check what the total is. And then you can discount items depending on if the total is more than $50, so on and so forth. And then you've got functions for handling the removing of an item and handling the adding of an item, which fetches another API call. And at the very bottom, we've got the lonely rendering logic, which has got the actual JSX, right?

Now, a lot of this is really core business logic. So if you want to test this, and we'll go into our app.test.jsx, which is just a VTest test here. Let me zoom in a little bit. I forgot to ask, can everybody see and read the code clearly? Right? Cool, thank you. So what we've had to do at the very, very top level here is we've had to mock the fetch. And then the way that, let's say we wanted to test whether or not a discount gets applied for more than $50 of basket, because that's the core logic that we're testing here. What we've done is we've rendered the shopping cart. And then afterwards, we're doing a query selector to check, is there text that has a strikethrough applied to it that says $10, which is the first element in our mocked price, and then we're testing it. So in a weird roundabout way, what we've actually cared about testing is very different to what we've actually tested, which is we tested UI with the goal of validating that our business logic works, right? It's just kind of unintuitive. But what we can really do quite easily is we can take a lot of that and make it into a humble object. And you'll see that the tests are far easier, but also the code looks a lot cleaner.

7. Simplified Testing with the Humble Object Model

Short description:

And you'll see that the tests are far easier, but also the code looks a lot cleaner. We've got a payments module with simple JavaScript functions for applying discounts, calculating totals, and shipping costs. In the app.jsx file, we call these functions from our payment service. Testing the core business logic becomes simple and doesn't require dealing with asynchronous behaviors or component lifecycles. We can spend less time writing tests and focus on checking the business logic.

And you'll see that the tests are far easier, but also the code looks a lot cleaner. So let's go here and we'll see the shopping cart now. So we've got none of those helper functions above, but we've got a payments that we keep on calling to actually do the business logic transformation. So let's take a look at payments.js and see what's there. You can see it's just a bunch of simple JavaScript functions. We've got an apply discount that takes in the items. You've got a calculate total that takes in the items and runs a reducer on it. You've got calculate shipping cost, which is just a very simple ternary within it. Everything has become very functional, small bits of business logic that we've included here. And then at the end, we just return that as a module itself. And if we go back to app.jsx, what we can see is all we do in our React component is we just start to call these functions that live in our payment service. And that's pretty much it. And then at the very bottom, we've got our actual JSX that's rendering. And when you go into the test file, you'll see it's very basic testing now. You're testing JavaScript. You're not having to worry about the asynchronous behaviors, or you're not having to worry about the component life cycles. You don't need to use testing library to test the core business logic that you have. It's very simple. So what that ultimately means is you spend less time writing the tests, and you can write more effective tests because you're directly checking the business logic. So we've got apply a discount for more than $50. It's very simple. You pass it in an items array, and it returns the items with an added attribute for the discounted price. And then some more stuff about no discount for less than $50. And the delivery label test, we're really just checking the function that takes in the items and returns the string for what the label is going to be. So we're testing what's really fundamentally important to us. Cool. I need to stop mirroring. Cool. So that is the humble object model. Let's look at the model view presenter.

8. MVC-like Architecture and the MVP Model

Short description:

This is a MVC-like architecture that GUI libraries will use. The view is a passive view that renders based on input values. A presenter is slotted between the model and the view to handle UI state and convert model data. The UI state and logic are moved outside the component into a hook called useComponent. Testing can be done on the hook or the UI component. The one hook per screen rule abstracts UI state and logic into a functional hook. The coding example evolves to use the MVP model with the payment object.

This is a MVC-like architecture that GUI libraries will use. And it's basically separating out parts of your GUI. You've got the model, which contains the business logic, it contains data fetching, and then you've got the view. Now, what we do here is we make the view a passive view. What that means is the view itself doesn't have any of its UI state or logic. All it does is it's kind of like a function. It takes in an input with certain values, and it renders accordingly. It's very functional in the approach that it does.

And in the middle, between the model and the view, what you slot in is you slot in a presenter. The idea with this presenter is that this presenter will contain all of your UI state. It'll handle converting the UI state. It'll handle converting the model data to a format and a shape that matches what you want to render in the view. So what does that mean in practice? If we look at our example here again, we've taken everything and put it into a humble object. What this also means is we're going to take the infracode, and also the UI state and logic, and also take that out of the component. And what we're going to do is we're going to put it inside of a hook that lives outside of the component itself. And this is going to be useComponent, whatever your component would be called. And it'll contain the infracode, but also your UI state and your logic. And it'll also make the references to your humble object as well. And now at this point, it's really at your discretion what you want to test. You could test the hook. Or you could test the actual UI component if you really wanted to make sure that the UI behaves in a certain way. A simple way that we talk about this, especially with the rest of the team, is a simplification to say you have a one hook per screen rule. What that means is each component of screen should only be calling a single hook at any time. And you abstract away all of your UI state and logic into that hook. And it really makes it functional. And we'll take a look at how we evolve our coding example to handle this.

Cool. So same exact app. But we're going to actually look at the MVP model. So we've abstracted away our humble object into payment.

9. App Hook and Simplified Testing

Short description:

Let's go back into app now and see what it looks like. We've created a hook called shoppingCart.hook.js, called useShoppingCart, that has moved the tough part of the component. The app.jsx has dropped down to about 38 lines. We still have our payment tests and have tested all the business logic. By abstracting everything into a hook, we only need one mock, simplifying testing because we don't have to worry about mocking all the dependencies.

Let's go back into app now and see what it looks like. So what we've done is we've created a hook called shoppingCart.hook.js. It's called useShoppingCart. That's the page that we have. And all of the tough part of the component has really just been moved over here. So we've got our use state here, the useEffect to populate the values. The calls to the payment service is moved here as well, so to the humble object. And then our callbacks to remove and add items. And then we've actually calculated the shipping cost. And what we do is we return an object in this hook, in this custom hook, that's got the transformed items. It's got the total and everything else that we need in the UI to actually render correctly.

And we'll go back into our app.jsx. And it's actually dropped down to about 38 lines. And all it does is it calls this hook and gets the data that's required to render from the hook. And then it just renders it very simply. It's very functional. It's coming out. And that's all that this does. And so what that actually fundamentally means is we still have our payment tests. We've tested all the business logic. But separately to that, if we did want to test how the UI looks, maybe we really wanted to make sure that the strikethrough was rendered when the original prices are no longer applicable and you have a discount. What we can do is we only need one mock here. And this is really fundamentally important when you start to have these complex components that need analytics and sentry and so on and so forth added in. You have a lot of stuff to mock. And by abstracting everything away into a hook, you always have at most one mock, which is the actual hook that you are passing into that specific component. And what we say to it is, in this case, just return this as the output of the hook. This is the data that I want my screen to get. And then all I'm doing is I'm rendering the app and then very quickly checking to see if this element exists within the DOM. Right? So it's far simpler to test it because you don't have to worry about mocking all the dependencies. It's just here's the hook and here's the value of the hook is going to return.

10. The Pit of Success

Short description:

The idea is to encourage your team and developers to fall into the pit of success, where well-designed systems make it easy to follow good patterns and be successful.

Cool. So why is this important? The idea with this is that you want to encourage your team and your developers to fall into the pit of success. So there's this great article that I would highly recommend reading which is called Falling Into the Pit of Success. It's by the co-founder of Strack Overflow. And he talks about how C++ is basically a pit of failure or a pit of doom. If you make one misstep in a C++ application, the consequences are massively dire and it doesn't do anything to safeguard you or protect you from falling into the pit of doom. Whereas a good programming language or a good framework or a good architecture, a good code base will set you up for the pit of success. The idea is that you create these well-designed systems that makes it really easy to fall into good patterns. You can still allow people to escape those good patterns, but you make it quite difficult to do so. So you encourage people to fall into the pit of success. It becomes very easy for them to be successful.

11. Recap and Importance of Teaching Principles

Short description:

Encouraging clean code and architecture can reduce testing waste and improve code organization and scalability. Isolating business logic into humble objects simplifies testing critical components. Using a presenter layer enables easily testable passive views. Teaching these principles to your team sets them up for success in testing complex components.

Great. So let's do a recap and my time is over. The four main points are, firstly, you want to encourage clean code and architecture and it can really help you reduce the waste in your testing along with all the benefits that it gives you in terms of your code base organization and scalability. If you isolate important business logic into humble objects, you can make it very easy to test the critical components to your app very quickly. Using a presenter layer can make easily testable passive views. That's another name for these views is the passive views and you can make it really easy to test those because they don't have any data dependencies. And lastly, and I think this is the key part, there will be a lot of more experienced developers here. You all know these concepts. These are fundamental things that you learn over the years of becoming a software engineer, but actually teaching these principles to your team and explicitly talking about them with your team will help you set them up for the pit of success and hopefully make it less daunting for them to test, especially when it comes to complex components.

12. Testing Used Component Hooks

Short description:

Testing a used component hook can be challenging and may require isolating difficult-to-test parts within the hook. Martin Fowler's blog suggests testing important bits in isolation and using end-to-end or smoke tests to ensure proper connections and wiring. While this part can be more difficult to test, combining it with a robust testing framework can provide code coverage.

Thank you all very much for listening. That is my Twitter and LinkedIn in case you want to stay connected. And yeah, I appreciate all your time. Kind of one of my favorite things about being an MC is that I get to ask the first question. Always. One of the things that I love is that I've done the used component architecture or pattern before. But how do you test a used component hook? With a lot of pain. Just blood, sweat and tears. Yeah, yeah, honestly. Don't try it. No. Well, you can test the hook. Obviously, you've isolated a lot of your difficult to test parts inside of the hook. There's different choices and it really depends. I know that's the cop out answer. Part of the theory that you'll read if you go into Martin Fowler's blog about humble objects is you're testing the important bits in isolation and then using some sort of end to end tests or some sort of smoke test, what you can do is you can make sure that all of the connections and the wiring under the hood works well. So combine it with a good end to end testing framework and you can make sure that your code is covered. But yes, you do have that part, which is far more difficult to test, right? For sure. For sure.

QnA

Importance of One Hook Per Component Rule

Short description:

A lot of questions revolve around the one hook per component rule. It's a teaching piece to get people to think about it, especially for junior developers. The rule is simple: don't add more than one hook for each screen or component. It's a way to introduce the MVP model and create a pit of success.

And so we've got a couple of questions. A lot of them are about the one hook per component rule. So I'm just going to kind of put them all together if that's okay, which is firstly, why is it important? And then how do you have any method of creating linter rules to enforce it? That's a good question. We've actually talked about the linter rules. We don't we don't have a linting rule. We kind of just use the code review process to make sure we stick to it. But I think it would be very valuable to have a linting rule for it. We just haven't invested the time to do it. Why the rule is why is it so important? I think it's not the rule itself. It's a rule that starts to get people to think about it. It's a very simple rule to give a junior developer who doesn't have much experience. To say to them, don't add more than one hook for each screen or for each component. It's a very simple way to start having this conversation. So I think it's more of like a teaching piece than anything else. It's not a hard and fast rule per se, but it's just a teaching piece. It's a nice way to formulate this concept of the MVP model. It's a pit of success. A pit of success. Thank you so much.

Following Patterns and Standardization

Short description:

We try to follow these patterns on Greenfield projects and aim to go in that direction on existing projects. Standardizing patterns makes it easier for people to work on multiple projects and helps with training. It definitely helps.

Okay. And someone asked, and I'm guessing the answer for this is yes, do you follow these patterns yourself? No, I'm a hypocrite. We try to, especially on Greenfield projects, we definitely try to follow these patterns when we're setting up the code base. On projects that already exist and we come into them, it's a bit more difficult obviously because they've got their own setups. But eventually what we try to do is we try to go in that direction. And sometimes it's more difficult. Sometimes it's easier. It really depends on what already exists. But yes, we really tried to do this. What that also means for our case, especially as a consultancy, is people will work on, in a single year, maybe they'll work on 12 different projects, right? It's short turnaround sometimes with projects. Some of them are longer, but a lot of them will be short turnarounds. And as you standardize these patterns, people will have a much easier time coming on and off the projects. And that's great for our clients as well because it means it's very easy to train people up because you've got rules and you've got consistent mind frames, mindsets and everything around it. So I think it does definitely help. No, that makes sense. That makes sense.

Avoiding Bureaucracy and Fostering Education

Short description:

As a tech lead, it's important to empower and educate others in implementing patterns and good practices. Avoiding bureaucracy involves striking a balance between enforcing rules and using education as a training tool. The collective wisdom of the team guides us towards maintaining a good code base.

So you've spoken a little bit about this, especially because you're not just doing it for yourself. You're a tech lead. You're kind of empowering others, educating others to implement these patterns and many other things in the scale. And how do you avoid bureaucracy with this? Like so many different people have so many different opinions. You often see in the questions as well, like how do you avoid that bureaucracy? It's an interesting question. Because yeah, there's a fine line between doing these mandates that are like, have this percentage of test coverage. You know, as an example, or these are the rules within our code base and we're going to stick to them no matter what. And there's another thing, teaching people about good practices and then using it more as an education piece. And I think the attitude that we have is it's an education piece. It's a training piece more than enforced rules. So you know, nobody gets a slap on the wrist if they don't use, if they have two hooks in their components or anything. It's very much a training piece. It's very much oriented towards learning and teaching. And it's more that the collective wisdom will guide us towards that ideal of having a good code base.

Using Custom Hooks and Sharing Business Logic

Short description:

As a leader, it's important to have the skill of ensuring only one hook is used. You can achieve this by creating a custom hook that calls others. This React pattern improves code readability and reusability. It's especially beneficial in a monorepo structure, where the same core business logic can be shared between separate React Native and React apps. Tools like TurboRepo or NX allow you to use the exact same logic without duplicating code. Abstracting hooks into separate modules provides additional benefits.

Yeah, that totally makes sense. Totally makes sense. I think especially as a leader, that's a good skill to be able to have. And the next question's back to the kind of one hook example. So how do you make sure you only have one hook? Do you create a hook that calls the others? Yes. It's a custom hook that calls the others. Definitely check it out if maybe this is a React pattern that is new to you. It's pretty cool and it makes your code so much more readable and reusable. So I personally would also vouch for it. One of the interesting things with this is if you work in a monorepo structure, let's say you have a React Native app and a React app that are totally separate from each other. Oftentimes, the core business logic is going to be the same. And if you start to take these and make them into custom hooks, what you can do with a nice tool like TurboRepo or NX is actually use the exact same business logic for your web and your mobile apps without having to code it twice. So there are other benefits to abstracting them away and putting them in their nice little own modules as well, which are byproducts of having a rule like this.

Signals and Software Engineering Concepts

Short description:

Could you use signals instead of... Not in React, you couldn't because signals don't have official support in React. But these concepts are like fundamental software engineering concepts. I don't think they're just applicable to React code. I think they're particularly relevant when you talk about React because it's this component based model that you can just chuck everything in a component.

Nice, nice. The next question with people love React here, people are very knowledgeable on it. And this is kind of a React question as well. Could you use signals instead of... Not in React, you couldn't because signals don't have official support in React. But these concepts are like fundamental software engineering concepts. I don't think they're just applicable to React code. I think they're particularly relevant when you talk about React because it's this component based model that you can just chuck everything in a component. But other frameworks might have a bit more opinionated patterns. If you go into Angular, you'll get an MBC model by default, at least when I used Angular back in the day. And so it's a lot more opinionated and it kind of guides you in those patterns. I'm sure you can implement something like this with Svelte or with Solid or whatever have you. It depends on answer for the session, right? There's always one. And I think this is one of the kind of interesting things when you come to talks. Because a person tells you about a technology and they maybe tell you a specific use case. But you kind of need a little bit from everything. And you kind of spoke about this, like React, React Native. And you're specifically giving this talk from the perspective of someone who is preparing tests and applications to fit into both worlds. And that may not necessarily be the same for you. So definitely knowing all the options on the table as well is quite useful.

The Danger of Multiple Hooks

Short description:

Having more than one hook per component adds more dependencies and makes it harder to follow a pattern. It's recommended to isolate multiple custom hooks in one single layer to simplify the codebase.

Let's go to the next one. This is, again, about one hooks. What's the danger of having more than one hook per component? Well, again, it's you're starting to add more dependencies. You can split hooks up into different hooks. Let's say you have two custom hooks that use different things. But it's just this concept of creating it simple to follow a pattern within the codebase. And if you have multiple custom hooks that need to refer to each other, I would isolate them in one single layer. Just because it makes it so much easier. It's very clear. There's one entry point for all of the data into your component. And then it just gets fed into the JSX, and is being rendered there. So even if you need multiple hooks, just put them all in one layer of a custom hook, I'd say.

AI Tools and Applying Concepts to Other Frameworks

Short description:

AI tools like Copilot and GPT can be useful for refactoring code and splitting business logic from JSX. They can aid in the code review process and automate mechanical parts of our job. While these tools are still a work in progress, they have the potential to make our work less time-intensive. The concepts discussed in this talk can be applied to other component-based frameworks, such as SwiftUI, but may not be as applicable to imperative UI frameworks like UIKit.

Cool. So now, from now on, if you do want to ask a one hook question, what I'm going to do is to tell you to check out Mo on the Discord in the speaker discussion room afterwards. But we do have a bunch of other questions. Like one, specifically, this is kind of about empowering your other team, and all these people, maybe people who are early in their career as developers. And so this person has asked, have you used Copilot or GPT to help everyone adhere to these patterns? Or to self-check that they have these patterns? And have you found any usefulness in it? Not in the code review stage, or in myself for the actual writing up stage. But it's been useful for refactors, actually. So if you go into a codebase, and you've got these components, and sometimes it becomes mechanical to refactor them into these one hook per screen rules, ChatGPT can be quite good at taking it and splitting up the business logic from the JSX for you in a pretty automated way, because it's quite simple and straightforward to do that. It's very mechanical once you learn how to do it. And this feels like very brand new, because all of these AI tools are pretty new. Even if they're being adopted really, really quickly, do you see anything changing in the future with the way we use AI tools to help refactor, or kind of self-check these? I think they can really aid with the code review process. So I've got a colleague, his name is Matt. He's been working on this GitHub library called Code Review GPT. You can search it up if you're interested. It kind of is this idea of, can we add it into the review process? It's still very much a work in progress. It's not perfected, but I think that's going to be really good in the aspect of it's not going to replace what we do on a day to day, but hopefully it can just make pointing out some of the more mechanical and methodical parts of our job, make that a little bit less time intensive, and automate that part of it.

Awesome. All right. We have time for one more question. The question is, you are a specialist in React, React Native, but people here, maybe they work in other frameworks. How can they take some of these patterns and things you've spoken about, and then you spoke about, especially when we talked about the signals question, but maybe what are some of the concepts that you would say, these are the things you need to remember and apply them into your framework? And maybe we can pair it up with the next question is, why is this talk a React Native one and not just React and Friends? That's due to my poor marketing and not thinking about how this can be applicable to other things. But in terms of how do you apply these to other frameworks, or would I apply them? I think so. Yes. And it definitely has a great place in these component-based UI libraries, like React. I think you can easily take this to other frameworks that are component-based. If you get into more opinionated front-end stacks, like if you, let's say, wanted to write a component within, say, SwiftUI, I think SwiftUI can also take a lot of these concepts and apply it to them, because it's very much based off of the React mindset, and it was inspired by that. But let's say you went into something like a UI kit application on an iOS app. It's a bit harder to do that, because the whole framing of how you design UIs is very different. It's imperative. It doesn't behave in the same way. So I think it's really applicable to other component-based frameworks. Thank you. You know, it's a great talk when the questions branch out into so many other possibilities. Thank you for showing us all of these possibilities.

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

TestJS Summit 2021TestJS Summit 2021
31 min
Test Effective Development
Developers want to sleep tight knowing they didn't break production. Companies want to be efficient in order to meet their customer needs faster and to gain competitive advantage sooner. We ALL want to be cost effective... or shall I say... TEST EFFECTIVE!But how do we do that?Are the "unit" and "integration" terminology serves us right?Or is it time for a change? When should we use either strategy to maximize our "test effectiveness"?In this talk I'll show you a brand new way to think about cost effective testing with new strategies and new testing terms!It’s time to go DEEPER!
TestJS Summit 2023TestJS Summit 2023
29 min
Component Testing With Vitest
Testing is important. Proper unit tests can eliminate the chance for bugs to appear. But which testing framework will be suitable? Let’s explore how we can develop a reliable and efficient strategy for component development and testing with Vitest
TestJS Summit 2021TestJS Summit 2021
20 min
It's a (Testing) Trap! - Common Testing Pitfalls and How to Solve Them
It’s a trap” - a call or feeling we all might be familiar with, not only when it comes to Star Wars. It’s signalizing a sudden moment of noticing imminent danger. This situation is an excellent allegory for an unpleasant realization in testing. Imagine having the best intentions when it comes to testing but still ending up with tests failing to deliver you any value at all? Tests who are feeling like a pain to deal with?
When writing frontend tests, there are lots of pitfalls on the way. In sum, they can lead to lousy maintainability, slow execution time, and - in the worst-case - tests you cannot trust. But it doesn’t have to be that way. In this session, I will talk about developers’ common mistakes (including mine), at least from my experience. And, of course, on how to avoid them. Testing doesn’t need to be painful, after all.
TestJS Summit 2021TestJS Summit 2021
8 min
Who is Testing the Tests?
Have you ever wondered: "who's testing the tests"? Of course, tests are only valuable if they catch bugs, but how would one validate that? Well, let me tell you about mutation testing!
Mutation testing is the act of testing your test verifying that they catch bugs. Of course, you can do this manually inserting bugs and running the tests, but a mutation testing framework can do this for you!
Join me and learn the basics of mutation testing and how to use StrykerJS, the mutation testing framework for JavaScript or TypeScript.At the end of this talk, you'll be the one that is testing your tests, and it won't even cost you much time!
TestJS Summit 2022TestJS Summit 2022
11 min
Using Tests for What?!
In the talk I will explain the pains and problems of form validation Then I will explain the mental model of unit tests, and compare it to how we think about form validations.I will introduce vest with a bit of live coding showing its unit testing syntax.