Creating an Organized React Native App

Mouhcine Smaha
OCP digital factory

--

React Native is a great framework designed to write one js code and build native Android/iOS app, if you are familiar with React js you can start your first app using react native. All notions of React are kept in this framework (components, hooks, context, …), also there is an SDK rich by basics components (input, button, drawer, theme, …).

There’s two ways to build your react native application:

  1. first using React Native CLI in this case, it’s obligatory to install Android Studio and prepare your machine to build app on your own machine. A Mac is required to build iOS app
  2. the second way using Expo CLI, only install Expo as global nodejs module and all is done. You’ll be able to build for Android and iOS under any development OS you have.

The Expo SDK built around React Native that help developer to build and published directly on the store (appStrore/ ios store). The Expo SDK provides access to device and system functionality such as contacts, camera, and GPS location.

Expo is not a requirement for React Native app it’s possible at any time to eject your code and continue under React Native without any intermediate, Expo offers you services and components already developed to speed up delivery and can make the web version during development, but if you need a custom access you should eject the source code. Keep in head to explore native system device its not the same way in all time for android and iOS, sometimes you should write two functions for the same feature (Android/iOS)

Get started with expo

Setup your environment with expo, react native

Assuming that you have Node 12 LTS or greater installed

# expo cli
$ npm install -g expo-cli
# NB there is an other way use React Native CLI Quickstart

It will be beneficial if you create your account on Expo first and install Expo client app on your mobile from App Store or Play Store. Expo client can run your app during development (through this client you can share your app to others mobile).

It’s true that Expo has a lot of advantages as I have already mentioned. but it also has limitations:

  • App size is not always optimal in relation to the implemented functionality
  • Not all iOS/Android APIs are available
  • The only supported third-party push notification service is the Expo notification service

Build & run

Create a new project MyApp by Expo CLI, use template argument to setup the project structure

$ expo init MyApp --template blank# --template(Valid choices are) ["blank", "tabs", "bare-minimum"]$ cd MyApp
$ expo start
# NB: when your app is started a QR will be displayed in terminal with local link to Expo server

Structure code generated

├── MYAPP
│ ├── assets
│ ├── App.js
│ ├── app.json
│ ├── babel.config.json
│ └── package.json

With your Expo client scans the generated QR code, automatically the app appear on your mobile, it will be reloaded to take changes of any edits code.

Expo allows running on a physical device, web or Simulator. It’s recommended to run apps using expo client cause of some native features are not supported by the web version.

HTTP server with graphical interface for Expo CLI
default screen generated by expo (web view)
# generate apk (android)
$ expo build:android -t apk

After executing the commend above, your js bundle will send to Expo Cloud Server, and prepare builds for Android/iOS, this operation takes a little time, if you don’t like waiting install Turtle CLI to build applications locally.

dashboard expo and your builds apps

Styling and Layout

A StyleSheet is similar to CSS. You can pass styles through style prop, but it’s often cleaner to use StyleSheet. The main advantage of StyleSheet that you can assure the quality and performance of your component, and separate style to render function, also StyleSheet show at compiling time any error or incorrect property.

Layout of the component’s designed by FlexBox algorithm. Similar to the grid system. Flex property defines how much your element can take from existing space. With FlexDirection (row, column, …) you align the direction of descending components.

By default Expo generates an initial App icon, if you want to configure your specify icon/splash update the value of the given keys in app.json.
app.json configures other more element in your app like name, API keys form needed services, resolution rules, to discover more click her.

Organize project structure

Inside of our demo app, I’m going to create two different screens to navigate: a Home and a Detail under screens folder. And Components folder for shared basic components

Let’s start with global configurations add eslint and testing
First step add eslint modules

$ yarn add eslint --dev

You should then setup a configuration file, and the easiest way to do that is to use the --init flag:

$ yarn run eslint --init

After that, you’ll have eslintrc file created and others plugin installed, then you can run ESLint on any file or directory like this:

$ yarn run eslint yourfile.js# or in package.json scripts add this : "lint": "eslint --fix *.js"

In this eslint configuration I chose Airbnb style guide.
Finally, you need to add ract-native as plugin into .eslintrc.js

extends: ['plugin:react/recommended', 'airbnb'],
.
.
plugins: ['react', 'react-native'],

To encourage better testing practices add React Native Testing Library, is a lightweight solution for testing React Native components. It provides light utility functions on top of react-test-renderer.

$ yarn add --dev @testing-library/react-native$ yarn add --dev @testing-library/jest-native jest

A best practice is to group unit tests of the same functionality in tests folder, which is at the same level as feature. Check that each test file has the name like file-name.test.js.

Use react native navigation to manage routing

React navigation is not required, but as most apps have multiple screens so you need a system to manage navigation between screens.

React Navigation is made up of some core utilities and those are then used by navigators to create the navigation structure in your app. Let’s also install and configure dependencies, then we can move forward with starting to write some code for Home and Detail components.

$ yarn add @react-navigation/native @react-navigation/stack$ expo install react-native-reanimated react-native-gesture-handler react-native-screens react-native-safe-area-context @react-native-community/masked-view

Create Home and Detail screens

Content of Home screen:
- we have a value number
- Button to increment the displayed number
- Second button to visit Detail screen
Content of Detail screen:
- The current and saved values
- Button to persist current value

(we will see how to persist data in the next part)

The navigation prop is passed in to every screen component, when you click on “Go to Detail screen” we call navigate function with the name of targeted screen and object param (key, value). To read the params in Detail screen component check route.params.

Home & detail screens

Finlay let’s update App.js component so we can correctly route our screens

Tow routes with HomeScreen as default one

Now you can navigate between screens with params, It’s important to understand what kind of data should passed, They should only contain information to configure the displayed content of screen. It’s not recommended to passe object (pass an object id instead of object), use the id param to retrieve data from API or global store (Context/Redux)

Our structure will look like this:

├── MYAPP
│ ├── ....
│ ├── screens
│ │ ├── Detail
│ │ │ ├── tests
│ │ │ │ └── DetailScreen.test.js
│ │ │ └── DetailScreen.js
│ │ └── Home
│ │ ├── tests
│ │ │ └── HomeScreen.test.js
│ │ └── HomeScreen.js

Best way to save data locally using ascyncStorage

First run these commands to add async storage dependencies

$ expo install @react-native-async-storage/async-storage

AsyncStorage is an unencrypted, asynchronous, persistent, key-value storage system that is global to the app. It should be used instead of Localstorage. it’s depend to target device AsyncStorage will store data into separate files or serialized dictionary for iOS and will use SQLite for Android.

Now don’t worry about your data, it’s possible to recover it after closing your app or shutting down the mobile.

New structure with services :

├── MYAPP
│ ├── ....
│ ├── screens
│ └── services
│ └── Storage.js
Storage service to R/W persist data

The unit tests can be broken when components use Storage service, it’s tightly coupled with its NativeModule part, it needs a running React Native application to work properly. In order to use it in jest test we need to mock Async Strorage. Don’t forget to add mock of any added native module.

# in package file
"jest": {
"setupFiles": ["./jestSetupFile.js"]
}

Manage centralized state using Context or Redux ?

With hooks Context API became easier to use. And has a short learning curve and less code than Redux.
Using Redux need (actions, reducers, store) and new libraries in your bundle. Context (provider, consumer) no need of extra libraries

There is no one better than other, but it depends on the frequency of updating state and the project size:
- Big project with many updates of state → Redux
- Small application where state changes are minimal → Context API

Check out the project template 👉 here 🌎

--

--