My plan is to progressively enhance the app into a fully complete app, allowing me to step-by-step learn important principles of development without overwhelming myself while at the same time practice my learning by applying them to a real app.
I mentioned in the previous post that I’m using React Native to build the app itself along with Firebase as the “backend”. I’ll be following as many best practices as I know for organizing my code and assets:
/__tests__/ - contains all of my Jest test suites. /.expo/ - standard Expo folder. /assets/ - static files(images mostly). /components/ - the reusable React components that make up the app. /constants/ - used for referencing screen dimensions and colors. /functions/ - houses Firebase Cloud Functions. /navigation/ - the React Navigation components. /node_modules/ - all the dependencies of the project. /screens/ - the screens which React Navigation uses for navigation.
App.tsx is the main entry point of the app. If the file extension confuses you, I’ll explain what it means in a later post. All you need to know is that it will compile to App.js and will be the “root” file of my app.
This app will have two major data objects – users and contacts.
A User represents the person that registers and uses the app. This User will have a number of Contacts assigned to their user profile.
A Contact represents a single person which contains their contact information.
The Realtime Database module in Firebase makes this really easy to visualize:
Remember, it’s NoSQL
What I like as well is that you can visualize your data in a hierarchical way.
The root of the database has /users/ as the container for the users of the app. Each user has their own uniquely generated ID which Firebase takes care of. Each user has their own /contacts/ and /me/ containers.
The /contacts/ container holds all of the individual contacts created by a single logged-in user. Each contact also has their own uniquely generated ID created by Firebase. In the screenshot you can see the information saved for each user:
- First Name
- Last Name
- Phone Number
The /me/ container holds the information for the logged-in user. It holds all of the same type of data as a single contact would. This allows for the logged-in user to “share” their own contact data with others(we’ll see this in action later).
When a user logs into the app they are first directed to the Contacts tab, which outputs a list of their contacts. Clicking on a contact brings you to a screen which shows all of the contact details and allows you to edit or share the contact.
The Data Flow
The gist of how this works is this:
- I log into the app and get redirected to the Main TabNavigator using a React Navigation function. The default tab is Contacts which will display a list of the contacts assigned to my user account.
- When this screen is about to load(using the componentWillMount() lifecycle method) the app calls a Firebase database function to ask for the contacts associated with my Firebase user ID.
- We await the response(because this is not instantaneous) and then use this.setState() to save the contacts into React State.
- We have a FlatList React Native component which generates a list based on the contacts data contained in state. Anytime the contacts within state changes, the list is re-rendered automatically.
- Each contact is represented by a list item within the FlatList component. Each list item has a key which links the list item to a contact.
- When you tap on a list item a React Navigation function is called to switch to a new screen to show the contact details. The contact details are carried over to the new screen as a prop using the key mentioned above.
- To reference the contact details in the new screen you can just use this.props.
That’s the basics of how contacts are loaded and displayed throughout the app:
- A Firebase user ID is grabbed once logged in.
- The contacts for that user ID is loaded via a database call.
- The contacts are stored in state
- A list is generated and automatically updated based on state.
- To update the list we just update state.
In Part Two I’ll discuss how validation and input “hinting” can make the app more enjoyable to use as well as limit mistakes made by users.