There’s an art to delivering production-ready applications using React Native. After all, mobile users are accustomed to high-quality apps and have great expectations. To help you, here are a few tips and tricks to enhance their experience:
The “happy” path is a well-known concept in software development where no exceptions or errors occur. As developers, it’s our duty to make sure we design solutions that keep users on the happy path. If they end up on “unhappy” paths, well, we end up with unhappy users.
Designing solutions that abstract UI states can help create deterministic app flows and more easily keep users within deterministic “happy paths”
Changes in UI state happen regularly, especially with network requests.
Usually, we see this pattern:
Tip: you can improve this by moving the request state to a deterministic set of strings:
This may seem like a small change, but we’ve removed the complexity involved in handling the loading and error states separately.
Here’s an example of how we can use requestState, for a download progress indicator:
By managing this state via a set of strings, we create a sequence of events with logic that can be managed and a simple experience for our users.
It’s important to note that requestState creates a foundation for providing feedback, as this will give users the necessary guidance for users to complete a task.
Network conditions will vary; not all users will be connected to wi-fi, so we should test for those scenarios.
Here are a few tips when the network is unstable:
An easy way to test is using Xcode’s “Network Link Condition” feature.
With this enabled, you can discover issues related to loading states and race conditions that could impact the user experience.
An optimal configuration is setting 2G Network - average; this makes the app’s requests slow enough to notice odd behaviors while loading.
When a network connection is unstable or lost, it’s best to present the user with an alert so they understand immediately why the app is not behaving normally. You can accomplish this by using libraries like react-native-netinfo and listening to network changes.
Finally, fetch requests should be executed outside a component; this makes it easy to manage when unmounted. Executing asynchronous fetch requests when a component has unmounted may cause its state to change, triggering memory leaks. Ideally, fetch requests should be abstracted with libraries such as Redux or MobX. By doing this, you’ll be able to easily handle errors and store in a global state.
Next up, let’s look at the interface. Users expect well-functioning UIs – slow navigation or interactions can cause users to dislike your app. In Android, one major reason why apps may feel sluggish is due to overdraw.
According to the Google developer docs:
“Overdraw refers to the system’s drawing a pixel on the screen multiple times in a single frame of rendering. For example, if we have a bunch of stacked UI cards, each card hides a portion of the one below it.”
However, the system still needs to draw even the hidden portions of the cards in the stack. This is because stacked cards are rendered according to the painter’s algorithm: that is, in back-to-front order. This sequence of rendering allows the system to apply proper alpha blending to translucent objects such as shadows.
This can be enabled by going to Settings > Developer Options on your Android device and selecting Debug GPU overdraw from the HARDWARE ACCELERATED RENDERING section
Here’s an example from the Google Developer docs. Tip: by reducing the amount of overdraw you are saving GPU computation, making your app less resource-intensive and creating a snappy experience.
It’s worth noting that these optimizations will also transfer over to iOS. Fortunately, when it comes to handling operations on the UI thread, React Native works very well out of the box.
When developing apps, we can only account for so many variations of user interactions; when bugs are reported, we sometimes get surprised by how they were triggered.
One way to catch these one-off interactions is to use monkey testing, which is a form of stress testing that adds confidence to the robustness of our apps. Android has a built-in solution for performing monkey tests with adb.
Tip: before running monkey tests on Android, enable Screen Pinning – this will prevent it from navigating outside the app.
This can be enabled by going to Settings > Security > Advanced > Screen Pinning and select “On.”
Here’s an example running a monkey test on the Google Play Store app from an emulator using this command:
-- CODE language-bash keep-markup --
adb shell monkey -v 4000
You can customize input events by providing different arguments to the adb command. A comprehensive list of arguments can be found in Google’s UI/Application Exerciser Monkey documentation.
The default arguments, however, capture a wide range of scenarios. This has helped me in the past to capture a few undefined object property errors.
When done running your monkey tests, you can disable Screen Pinning using this adb command:
Monkey testing is a great way to discover bugs that are hard to reproduce. By identifying these one-off bugs you can increase the stability of your application.
Here are a few tips to display a better message:
Using libraries such as react-native-error-boundary you can show a message to the user explaining that something went wrong. By doing so, you give users a quick solution to get back to their task, thus making the errors more forgiving.
The library react-native-exception-handler is used to capture these exceptions and present a message that something went wrong and give users the ability to restart.
Unfortunately, iOS has a limitation with restarting apps; users must do it manually.
Lastly, it’s important to pair these solutions with reporting tools like Sentry to capture the exceptions and address them as hotfixes so they do not affect others.
Above are a few ways to make sure your app is ready for production and will deliver an exceptional user experience. Providing feedback throughout their journey when things go wrong is especially important so the user can continue on to accomplish their objective.
We’d love to learn more about your project.
Engagements start at $50,000.