Published: May 2, 2022
11 min read
In this article, you'll learn:
1
✅ Top Refactoring Use Cases & Handling Examples
2
👨💻 Expert Hacks: How to Make Refactoring Easier
3
🚀 Our Expertise: Numina & FitTracks
4
💡 Takeaways
Developing an app isn’t just about a one-time effort — its success among users significantly depends on continuous updating. For updates to be smooth, it’s reasonable to “clean up” the code from time to time by doing the refactoring. Freshening up the code without changing functionality substantially improves the app’s performance and enables app updating with fewer problems.
A “Refactor React Native App” guide from Stormotion - welcome! (image by Al Power™)
Surely, there are certain rules when it comes to refactoring your React Native code. First of all, it’s best to do refactoring gradually — modifying big chunks of the code at once might lead to even higher code complexity. Plus, the developer working on refactoring should be well-acquainted with the code and, ideally, have worked on it previously.
However, with the right approach and enough patience, it’s likely to make your code more understandable and ease the app’s maintenance in the future.
In this article, we’ll talk about the most widespread refactoring use cases and show you exact ways how you can do it. Additionally, we’ll give you a couple of hacks that Stormotion developers use to make refactoring easier and share some real-life cases from our experience. So, if you feel like your React Native app could use a little polish or you’d like to be set & ready for refactoring in the future — welcome! 🚀
Everything mentioned in the article is applicable to such JS/TS concepts as functions, components, hooks, and classes. So, as not to confuse you, we’ll be using the word “unit” to define all of them.
In this section, we’ll talk about cases when refactoring is most likely applicable and give you exact examples of how you can improve the code in such situations.
There are two types of code duplications: let’s call them visible and invisible. Visible ones are practically code blocks or line repetitions that recur in different parts of the code. They’re pretty easy to deal with since you can spot them right away.
Invisible ones are the real problem. You can find them only by getting deep into the code’s structure and spending quite a while examining it. They imply having several units that lead to the same result (are responsible for the same functions) but are written differently.
Both types can be harmful in several ways:
We actually have an article on covering the technical debt. So, in case you’d like to learn more about the topic, feel free to follow the link:
To solve this problem, you can create abstractions that generalize similar code blocks. To put it another way, we extract repetitive parts into separate reusable functions and can substitute these parts with their new shorter forms – function calls.
For example, we could place PhoneInput and CodeInput units into one generalized TextInput component by taking out common parameters, styling and error handling.
Another case of code refactoring is simplifying unnecessarily long and/or complex codes to make them more functional and less bulky. Even though it’s not possible to set a specific number as a golden standard for code length, we determine the necessity of simplification by whether or not a unit’s length exceeds a certain limit (300-400 lines, per se).
Simplification here implies taking a part of the logic and dividing the massive UI into several independent units.
React code and component logic overcomplications are one of the main aspect to pay attention to while refactoring (image by TechCrunch)
However, you might face a problem when trying to perform such a separation. Often, it’s not possible to divide the complex unit because its logic is directly connected to this specific structure and, thus, can’t be separated as a function or put into an individual component. Typical examples for such an issue are animations, processing of certain forms, data loading, etc.
Luckily, we can easily solve this problem using the so-called Hooks that are a part of React functionality since version 16.8. Hooks allow us to divide a component into small functions by their purposes, and not based on the lifecycle method. For example, you can move video loading and playback logic from one lifecycle method to separate hooks. Moreover, using your custom hooks, you can extract the above-mentioned video handling logic and side effects out of the component so it's easier to test and reuse.
There was another case where we needed to handle various BLE-based protocols within a React Native gym app. Our solution was a generic protocol handler that helped us avoid code overcomplication. You can additionally control components’ inner state by using a reducer — it’ll make its behavior more predictable.
To make your system work more efficiently and with less resource consumption, optimizing the code as a part of your refactoring strategy is definitely worth attention. Let’s take a look at a couple of techniques that you can use for code optimization.
All units in React Native have a specific state. And, when a user disturbs the state by a certain action, it normally re-renders. However, units changing require apps to work extra intensively and thus influences their performance. Luckily, unit restructuring isn’t necessary and can be avoided.
Functional components of the react code can can be overly “heavy” so you can use react refactoring tools to fix the problem (image by Blaze)
For example, the useCallback hook allows us to get back a memoized functions’ version that changes only if one of its dependencies changes. It’s useful in case you transfer callbacks to optimized React child components — these rely on the equality of the references so as to prevent excessive rendering.
Another way to reduce re-rendering frequency is by wrapping a component into a React.memo (a higher-order component) call. It’s reasonable to perform in case your component keeps rendering the same thing with invariable props. This way, you can improve the app’s performance and memoize the result. It means that in the future, React will use the last rendering result to avoid its repetition.
For repetitive calculations issues, React created the useMemo hook. It lets you memoize the result of the function call between renders and avoid repetitive resource-consuming calculations.
Functional components that cause repetitive calculation can be fixed by using react hooks (image by Hackages Blog)
The first needed parameter would be the function that is to be memoized, the second one is the dependencies array. In case one dependency is changed, the React hook will automatically recompute the whole function.
But surely, don’t forget about keeping a balance. Optimizing itself requires resources as well, thus, it can be excessive in some cases. For a deeper understanding, we highly recommend taking a quick look at this article — it has a lot of practical examples.
As we also do refactoring at Stormotion, our developers have found several ways how you can make it less of a challenge. In this section, we’ll share them with you.
It might be a great idea to create a separate feature branch to perform refactoring (especially relevant for large-scale refactoring). It can be feature/refactoring
branch, for example. Once the feature is done, you can simply merge this separate branch into the main one.
Doing refactoring in the same branch might lead to even more technical debt (image by Perforce)
To avoid multiple merging conflicts when several developers are working on the same parts of the code, you can rebase your branch onto the main development branch daily. Rebasing implies moving the starting point of the branch.
It works in a way as if you would create a new branch and gradually implement all new commits in it every day. Nevertheless, in case a conflict occurs, you won’t have to deal with it within the final state of your files, where you can’t really track when and what was changed. Luckily, you only need to pay attention to the files state at the moment when the conflict occurred.
Most of the Integrated Development Environments (IDEs) have a default configuration for code formatting. Consequently, it often happens that companies don’t really pay attention to the way code formatting happens. This, in its turn, can lead to code style inconsistencies.
To avoid such an issue and keep the code’s style consistent, we recommend using ESLint and Prettier tools in all your projects. ESLint is meant to find and fix code problems not depending on their source. Prettier is built specifically to perform code formatting and thus handles it slightly more targeted.
Software complexity can be high due to different code styles (image by DigitalOcean)
In case you’ve implemented hooks in your project, we recommend using the exhaustive-deps rule that is a part of eslint-plugin-react-hooks linting rule. It warns you when dependencies are set up incorrectly and offers a solution.
In fact, many IDEs have some automatic refactoring features. Such IDEs are called refactoring browsers. Here are a couple of those that JavaScript/TypeScript supports:
Refactoring your React Native App can be simplified by using automation tools (image by JetBrains)
The concept is pretty simple — you set up an automated refactoring feature within the software and tell the software how/how often it should be performed. It can be removing duplicate codes, fixing issues with headers, etc.
As a software development company, Stormotion surely has a lot to do with refactoring. And in this section, we’ll share 2 of the most representative cases from our experience.
Numina is an app that helps with developing leadership skills. It includes a bunch of short lessons/courses and is suitable for both individual growth and team building.
This React Native project was developed using the Expo tool. The key refactoring goals were:
We’ve worked on the old code of the Numina to lower technical debt (image by Numina)
The most challenging aspect of the refactoring was updating the react-navigation stack — the two versions were significantly different and included a bunch of breaking changes.
We hand to fully rewrite the navigation by carefully transferring the attributes of the old navigation into the new one. It also happens that some parameters simply didn’t exist in the new version. So, we had to look for other ways of implementation.
Our second case is a fitness application with voice-guided workouts FitTracks. What’s special about the app is that its exercise programs are recognized by the German government as prevention courses. It allows users to ask their health insurance companies for full reimbursement of subscription costs, for example.
If you’d like to learn more about the development of the app itself, feel free to follow this link to a detailed case study on our website:
Refactoring of the FitTracks project implied:
Apart from that, we took the whole project’s logic out of the contexts into small separate hooks. We removed the excessive optimizations and got rid of inline styles. Additionally, we slightly changed ESLint and Prettier configurations since we’ve got a deeper understanding of these tools and found some ways to improve them for the project specifically.
React hooks are a great way to make refactoring in projects like this easier (image by Stormotion)
While working on this project, we’ve felt the importance of not changing the code too much since it could damage the business logic. To make it as careful as possible, we’ve performed a thorough refactoring planning so as to break the process into small steps and not do anything excessive.
Even though refactoring is a challenging process, it’s worth it in most cases. It can help you reduce costs, prevent issues, deliver future updates faster, and generally have a better performance of your app with less resource consumption.
Let’s briefly sum up what we’ve talked about in this article:
In case you have questions left, need help with refactoring your solution, or want to contact us for any other reason — feel free to do it anytime!
Was it helpful?
Read also
Our clients say
They make the whole business work for us, and their improvements are fundamental to our operations. They’re reliable, honest, and willing to try new things that will help us. We appreciate how flexible and easygoing they are.
Pietro Saccomani, Founder
MobiLoud