Learn React with a real world problem : A quick route search app
I took on the challenge to learn React not with a traditional TODO app but to solve an interesting problem that involved finding flights having multiple routes to reach to a destination from a given source. I am sharing this story so that someone beginner in React (As I got to learn multiple features of React working through it) could use this as a learning resource with a real world problem. I really enjoyed working through it as this not only has the flavour of UI development but also some nice problem solving twists with algorithms as well. Gone are the days of “Hello World” and TODOs !
The Problem
Here, we want to implement a flight search feature. From the UI standpoint, user can input and search for the flights. Take the user input from the search form and perform a search on the flight data that is present as a JSON for this exercise and display the valid search results in the results section. Flight result should contain both non-stop and multiple stop(with layover) flights. The problem has considered 4 airports and published a schedule as a JSON with required information like source, destination, departure time, date etc.
Let’s Begin
I am writing about the major features of React that’s being used to implement this app. A good way to follow along is to refer the code from this github repo.
We are intended to make it with a very basic responsive UI. On the landing page, there is a search form to enter Source and Destination city along with the date of travel. There is a result section to display the available options.
I have explored the following features of React and state management to create this app.
- CRA (Create-react-app) for scaffolding.
- Code modularity by using Component architecture.
- Separation of business logic from UI by State management and utility functions.
- Responsiveness by React-bootstrap layout and components.
- Unit tests using Jest and enzyme.
Identifying components
Once we create a basic up and running app using CRA, the next task is to break down the problem into smaller sets by identifying lower level and higher level components. In React world, you’ll mostly hear about smart components and dumb components. An ideal app should have more of dumb components to promote reuse of code and easy testability. I would recommend reading the official documentation to think in terms of React components here.
For the purpose of this application, we have broken it down into the following components based on the requirements.
Search Form: This is a smart component (marked in blue rectangle) connected to the store and it’ll update the state of the app based on the user interaction.
Flight’s Grid: This is a dumb component marked in green, that displays a list of flights as a result of search. Interesting thing about separating this is that we can reuse the same for return flight info as well by just changing the flight data that we are passing to the component. Did you see how magically we can do this feature by just using a component ??
Flight Info: Again a dumb component but to show the individual row of flight info including the logo, flight carries, time and pricing info. Interesting fact about this is that we can reuse this to display a flight with layover info as well.
There are some more granular level dumb components as well to do a clear separation of concerns. It may look a little over engineered, however my personal experience is that when the app scales, this strategy will make the code a lot more maintainable.
Also you may be able to notice the use of functional syntax for creating components vs class syntax. The use of React hooks allowed us to introduce state within functional syntax as well. Below are the examples of useEffect and useState hooks. useEffect is more of a replacement of the lifecycle method available in React Class syntax and useState is a replacement of setState method that allows us to make the functional components stateful.
State Management
State management solutions like Redux and MobX comes really handy to maintain a consistent state of the app by keeping it separated from UI layer and provide a standard way to transition the state. For the purpose of this app, I’ve used Redux along with Redux-saga as the middleware.
Redux-saga makes it really easy to handle multiple nested action calls and store updates by virtue of its generator function syntax. Also it is easy to test as they did not rely upon Promises. You may read more details on the differences here. A glimpse of how simple it is below.
Route Search Algorithm
Ideally, all the mess of searching the desired data should belong to back-end and this kind of operation is an ideal candidate for a service oriented design. However, for the sake of learning the power of Javascript, I coupled it here as a utility module.
Looking at the flight schedule JSON data, I see that it can be nicely represented as a graph with Airports as nodes and individual flights as edges (as depicted below).
Once the data structure is identified, it is easier to find the related algorithms to traverse it and find the relevant data. If you look at the code in flightManager.js utility, you can see the use of connectedNodes function to convert the flat array of object data into a connected graph and the explore function is used to traverse the graph with all connected edges from source to destination nodes recursively. Then I have filtered it based on some rules like the minimum layover time of 2 hours or whatever and to strike out cyclic flights between interim nodes i.e airports. The traversal method used in this solution is BFS (Breadth First Search). This traversal method allow our search algorithm to scale for n level of nodes and easily introduce custom business rules.
As the results given by the traversal is more in terms of edges, I have used array transformation to make it business friendly and bifurcated it into a non-stop flights array and a multi-connect flights array. This help us to draw the flight grid with both options and you may be able to display them if you wanted to display it in a sorted order of all non-stop flights first and remaining flights afterwards.
At the End
As you might be able to perceive here, the idea is to learn smaller aspect of technology and a thought process to apply them in your problem solving. The traditional approach of breaking down the problem into smaller pieces makes it easier for you to identify the best solution for the part of the problem. I was able to stitch these pieces together to complete the bigger vision of the app. You might appreciate the way how the lower level components come together and being reused to form bigger pieces of functionality. Write about your feedback if you’re able to learn something or having different ways to tackle the same problem. Would appreciate a mutual learning discussion here!