Advance React Navigation: Handling Complicated Flows Like a Pro
Editor's Note: This blog highlights Charlotte, a React Native Tech Lead at Feodo Apps, as she delves into the intricacies of React Navigation. By integrating state machines and custom navigators, she demonstrates how to streamline complex app flows, making navigation simpler and more efficient for developers.
Hi everyone, I’m Charlotte, a React Native Tech Lead at Feodo apps, and today we’re diving into a topic that impacts all of us—flows. Flows are the backbone of many apps, from onboarding sequences to complex payment funnels. But when these flows grow in complexity, they can become an overwhelming challenge for developers.
When Simplicity Turns Complex
Most apps have flows—a structured sequence of pages where we control the user’s journey. In a perfect world, this structure would remain manageable, but as complexity creeps in, it’s easy for the code to become convoluted. I faced this firsthand when I joined a new project with already implemented flows. My task? Add a simple page to an existing flow.
It sounded easy, right? However, the reality was far from it. The flow’s code had grown into a tangled mess. The challenge wasn’t just adding a page—it was deciphering the logic hidden beneath layers of complexity. As the sole developer tasked with this, I found myself lost in the maze of navigation paths.
Fast forward a few months—I had the opportunity to start fresh on a new flow. This time, I was determined to make it right from the start. Today, I want to share how we leveraged the power of state machines to clarify React Navigation and simplify even the most complex flows.
But before we dive into the solution, let’s take a step back and define what we mean by “flows.”
What Exactly is a Flow?
A flow is a predetermined series of pages where the user’s path is predefined, like an onboarding sequence or checkout process. Unlike regular navigation, where users are free to jump from page to page, a flow guides users along a set path, ensuring they click "Next" to reach the subsequent step.
However, things can get messy when logic and conditions start piling up within a flow.
Consider the flow I had to implement—it looked simple on the surface, but was riddled with conditions and logic. The problem? Using regular page-to-page navigation for such a flow made it almost impossible to get a clear overview of the entire sequence. You had to dive into every page to understand the navigation paths hidden within conditional logic. This made debugging and adding new steps a developer’s nightmare.
So how do we fix this? How can we write code that screams, “These are all the possible pathways!”?
The Ideal Flow Solution: State Machines to the Rescue
Imagine an ideal solution—a system where you can see the entire flow in one place. A system where pages don’t have to “know” which page comes next. Sounds perfect, right?
Well, this is where state machines come in. State machines allow us to define the entire flow—every page and every transition—in one clear place. Let’s break it down with a simple analogy: traffic lights. A traffic light system is a state machine, where each color (red, yellow, green) represents a state, and transitions occur based on predefined events.
In a flow, our states are our pages. Each time the user clicks "Next," we send an event that transitions to the next page. This way, all the logic and pathways are stored in one place—easy to read and understand.
The First Step: Implementing State Machines in React Native
Let’s visualize this. We define a state machine with our flow's steps—pages A, B, C, and D. Each time the user hits "Next," we transition from one state (page) to the next. This is a great start, but there’s a problem—our state is split between the state machine and the navigation.
Whenever we transition states, we also need to update the navigation to display the correct page. If these two states get out of sync, we risk bugs. It’s easy to forget a navigation update, leading to issues that are hard to track down.
This leads us to our next evolution: a more declarative approach. Instead of defining pages inside a traditional navigation stack, we define them based on the state machine’s value. This eliminates the need for manual navigation calls, making the flow much more robust.
But we still encountered issues—most notably, losing out on transition animations. And even worse, we still faced potential bugs due to configuration mistakes.
And then came the breakthrough: building a custom navigator. One of my colleagues asked, "Why not create a navigator tailored to our specific needs?" And that’s exactly what we did. By building a custom flow navigator, we were able to combine the state machine and navigation into a single, coherent system.
Let me introduce you to the Flow Navigator.
The Flow Navigator looks and behaves like a regular navigator but with one critical difference—you define the flow’s order in one place. Here’s an example: cart review, payment, success. In one glance, you can see the entire flow and its order. And inside each page, like Cart Review, you simply call a method—goToNextStep—to advance to the next page without worrying about its position in the flow.
Internally, this works through an index that tracks which page is currently active. As the user moves through the flow, the index bumps up, ensuring seamless navigation from one step to the next.
Handling Conditional Logic with Ease
Of course, not all flows are linear. What about conditional pages—like a document signing step that only appears once? With the Flow Navigator, we can handle conditional logic effortlessly. For example, we use a Boolean value to determine whether the document signing page appears or not.
Once the condition is met, the page is removed from the flow, and the user moves on. This allows for dynamic, adaptive navigation based on user input or other conditions.
Limitations and Future Possibilities
The Flow Navigator solves many architectural issues, but like any solution, it has limitations. For instance, you can’t always see all the possible pathways at a glance. And while it works well with React Navigation, we haven’t built it for Explore Router yet. However, these are challenges we are actively working on.
Rethinking Flow Architectures
Today, we explored how state machines can transform complex flows into clear, manageable systems. By building custom navigators like the Flow Navigator, we’ve created a tool that makes navigating complex flows easier and more intuitive.
I hope this reflection on flow architecture has inspired you to think about your own app’s navigation in new ways. Next time you face scattered logic, remember the power of state machines and custom navigators—they could be your key to clarity and simplicity.