Navigating the Transition to React Native: Insights from Building a Time Tracking App
- Tim Millar

- Sep 3, 2025
- 9 min read

React Native has the advantage of enabling the creation of apps that can be used on Android devices, Apple devices, or in a browser for many use cases. When trying to add a modern language for UI programming to my skill set, it seemed like React Native was a good choice since it covers this wide marketplace of platforms.
I have been a software engineer for over 30 years with an emphasis on backend development in languages such as C#, C++, SQL Server, Go and Postgres. In this blog, I will explore learning React Native from the perspective of someone with this type of a background. This will emphasize the areas where I experienced a higher learning curve than a front end developer would have had
with some of the new concepts and terminology.
In the following discussion, a component can either be a screen displayed to the user, or a reusable screen fragment that is part of a screen.
Let's start by reviewing one aspect of React programming that will aid in understanding other concepts within React. At React’s core, there is a reactivity loop built into a program so that if the state or properties of a component change, a re-rendering of the component (and possibly its children) is scheduled. This loop is outside the direct control of the programmer, so it’s important to understand how it works to be able to cause the components to refresh when desired and to troubleshoot when it’s not behaving. A property (or prop) is a variable passed into a component when it is navigated to. React is a declarative programming language where the desired results of the components are described in the program, but the details of how it is implemented are left to the React engine, etc.
Hooks
Due to React's declarative nature, it uses hooks to interact with state (memory) and lifecycle features. These hooks are functions which by convention start with “use” and provide access to a variety of functionality for your code. You can also create your own custom hooks for reusable logic.
These are hooks discussed in the next section that have to do with the data used within a program:
- useState
- useReducer
- useContext
State, Reducers and Contexts
In React, one will see what looks like variables defined as constants (const). The reason is that the values of these memory locations should not change for the duration of a single rendering. The next time the component is rendered however, they may have different values. Functions are also declared with the ‘const’ keyword.
When data needs to be stored between renderings of a component, and that data is only pertinent to one component, it can be defined in the following way:
const [showTimePicker, setShowTimePicker] = useState(false);
The useState function returns an array of two items: the state field (showTimePicker) and a function to change the value of that field. The value of the state constant (showTimePicker) should never be changed directly; instead, the "set" function (setShowTimePicker in the example above), should be called with the new value. This would then cause the component to be re-rendered at the point determined by React. The initial value of the state variable is set as the first parameter of the useState call, false in this example.
If the state data needs to be shared between components, a Context can be created instead of using the useState hook to handle the updating and retrieval of state data. The advantage of using a Context is that data can be shared between components, even if they are at various levels within the component tree. Within a context, a reducer pattern is typically used to decouple state, actions (used to update the state), and logic. This pattern allows for clean, scalable, maintainable code. The reducer function within the reducer pattern is a “pure” function that never changes the state but returns a new copy of the state with updated data. All the logic to return a new state is done in this single function within the context. This ensures there are no side effects to creating the new state.
It is possible (and can be a best practice design) to combine state variables using the useState hook (for data local to one component) and one or more contexts within a component.
Effects
Because of the declarative nature of React, a mechanism is needed to allow programmers to execute logic at certain points in the life cycle of the component. The useEffect hook is that mechanism. This is an example:
useEffect(() => {
fetchTimeSheetEntries(state.user, firstCalendarDate, lastCalendarDate);
}, [state.fetchNum, state.user]);
In this example, the Time Sheet entries are fetched from the server. The first parameter of useEffect is a function that takes no parameters and calls fetchTimeSheetEntries with three parameters. The second parameter of useEffect is an array of dependencies that stipulate when this action is to be performed. In this case, it is when the fetchNum (an integer) or the user changes. It is also invoked when the component mounts.
If no dependency array is specified, the effect function runs on each render. If an empty array, [], is specified, then it will run once when the component mounts.
The App
After taking a class in React Native, I wanted to create a simple app to enhance my skills. I chose to create an app that allows users to enter time spent on work. Our company has been using Freshbooks for time entry, accounting, billing, etc. The app has an option on the Settings screen that allows the user to choose whether the backend will call the Freshbooks APIs for time entry functions or the data will be stored in a MyndCore database.
The first screen displayed (provided the user is already logged in) is a month-at-a-glance view that shows a summary of time entered for a given month. Within a single day, the total hours for that day and the number of entries is displayed, if any. At the bottom of the screen is the total hours for the month plus a list of the weekly totals. Partial weeks from surrounding months are included in the calendar and weekly summaries at the bottom, so the sum of the weeks may not add up to the monthly total. If the + is tapped, the Time Entry form (shown further down) is displayed, defaulting to the current date.

If a day is tapped on the month calendar, a day view will be displayed with any previously entered times shown including client, project, hours and an optional note if there is room to display it:

An entry can be deleted by tapping on the trash can or be edited by tapping on the item. Alternatively, the + can be tapped to enter a new time entry defaulting to that date. The Time Entry form is shown for both new entries and updates and has a couple differences to highlight which one it is.




Both the client/project and service fields will display a list of available, tappable clients/projects available for the user, and services available for the user. For the current date, if the start time is not entered and the duration is, the start time will be calculated back from the current time based on the duration.
Builds and Deployment
When I got the app more-or-less working as I wanted, I thought I was close to being done. Wrong!
In order to generate the Android App Bundle (.aab) file for deployment I ran the following command: "eas build -p android --profile production". EAS is Expo Application Services and uploads the source to the cloud and performs the build there. If the build is successful, you can download the the .aab file which can be uploaded to install to the Android Play Store. EAS provides commands to upload directly to the Play Store, etc., but I did that through the Play Store site.
The courses I took didn’t mention anything about deployment or getting the app onto the app store. I only focused on Android and didn’t do any work to get the app ready for iOS, even though the coding, especially when using Expo, requires little effort dealing with differences on the different platforms. There is a small one-time registration fee to create a Google Play developer account that will let you upload new and updated apps going forward.
The questionnaire to fill out for a new app is quite extensive as Google wants to monitor what they make available on their Play Store. Apps are scanned for malware, viruses, and security risks.
When I first uploaded the app and installed it on my phone, I couldn’t get it to work. I would tap on the icon and nothing happened. After installing Android Studio, I had access to the logs on the phone. While looking into the problem with AI’s help, it seemed there were differences between running locally using “expo start” in the terminal and deploying to the play store. This was one of the keys: Running locally with “expo start” makes use of a development server and uses the Metro bundler. It interprets code directly and might handle some edge cases more leniently (than when deploying to the play store). After more digging, it seemed like some of the dependency versions weren’t compatible with each other and this command bailed me out of trouble more than once: “npx expo install”, which installed versions compatible with the version of expo, and also compatible with other dependent modules.
My main development machines are Windows boxes and I’ve been using Visual Studio Code. When I first started the react native classes, I was having more success getting the programs to run under WSL (Windows Subsystem for Linux) and using Yarn for the package manager. But after I installed to my Android phone and couldn’t get things to work, I installed Android Development Studio on my computer so I could get the error logs off my phone and do better troubleshooting. This resulted in abandoning WSL and I finally got my setup working under Windows, with VS Code, and using NPM as the package manager. Each time when switching environments, I had issues getting the code working again, probably having something to do with where I had packages installed and so on. Once I had things setup better, I could run on different Windows boxes or a Linux box with the same results.
Backend and Interfacing with Fresh Books
I have an Express API service running on a Linux box. When it starts up, it connects securely to an NGROK web server (see https://ngrok.com/) and forwards incoming API calls to another local port where my API endpoints are listening. This enhances security and is simple to setup. From the app’s perspective, the endpoint is an NGROK one. For storage, the code uses a Mongo DB in the cloud. After this was working, it was time to interface with Fresh Books instead of the Mongo DB.
For the most part, this was straightforward. AI was invaluable in getting everything to work throughout the project, but especially with this phase. At a high level, the design was that the react native app would make API calls to my service running on the Linux machine (via NGROK). The app has a settings screen where the user can select whether or not they want to interface with Fresh Books. If they do, the server code makes API calls to Fresh Books rather than writing to and reading from the Mongo DB. The Fresh Books API instructions site was fairly straightforward and writing this code was pretty simple. However, to allow the user to authenticate on Fresh Books, meant that the app needed to use a react native webview to login. This meant that my code didn’t need to necessarily know the user’s login or password.
Before I could hit the Fresh Books endpoints, I needed to login to Fresh Books (from the app) and setup configuration for my app on their system with the appropriate scopes to define access needed and URI redirection endpoints, etc. When I first did this, I set this configuration up under my account as a MyndCore employee. I later found out by reaching out to their tech support that this needed to be under the owner account for proper authorization. Before doing that, I could read, but not write to Fresh Books.
On the app, if the user selects to use Fresh Books and they are not currently logged in, the app will navigate them to the Fresh Books login web page where they can authenticate. From there the redirect URI is used to capture a code which is used to get a token, etc.
Overall, I had a positive experience developing in React Native using Expo and the end result was similar to what I had envisioned at the beginning. In a later version of the app, I wanted to add a dictate icon to the month-at-a-glance screen and allow the user to verbally give a description of the time entry and have the New Entry screen appear pre-populated with information that was dictated. For this, I chose to implement a neural network Python solution on the back end. In my next blog I will discuss some of my successes and challenges when coding this using a lot of AI help.
About the author, Tim Millar:
I am a senior software engineer with over 20 years of experience designing, developing and implementing applications primarily in the financial industry. The tech stack I've used has included C# and SQL Server on Azure and Go and Postgres on AWS. Feel free to reach out to me directly: tim@myndcorepartners.com

Comments