Main Learnings:
- React Native, React Native libraries, JavaScript and JSX
- Expo CLI, Android and iOS emulator
- React Native navigation, large lists, user input and interactivity
Table of Contents
Introduction:
In order to learn React Native, you must have a solid understanding of HTML, CSS, JavaScript, Node.js and npm. Knowing React would be an asset. React Native is built using JavaScript, and when building user interfaces (UI), HTML and CSS will be involved. Node.js and npm will be necessary to import and use other packages in your application. Packages are a collection of files that make up a module, which you can freely use in your application. If you are unfamiliar with HTML, CSS or JavaScript, refer to this post. If you are unfamilar with Node.js or npm, refer to this post. If you wish to learn React, refer to this post.
React Native, developed by Meta, is an open-source library that is widely used for mobile application development. It is popular due to its ability to utilize one codebase that works on both iOS and Android devices, using fully native components. The JavaScript is translated into the platform's native code, allowing for high performance and efficiency of the application.
React Native saves time, costs, and allows for quicker development by utilizing one codebase. Instead of 2 separate teams to develop 2 mobile apps, only 1 team needs to be hired. This eliminates the need for constant communication between teams, increases development of the application and reduces the time to market. Another benefit is that if you understand React, React Native is very similar and can be picked up very quickly. Vice versa, if you learn React Native, React can be quickly learned.
Setup and Structure
To get started with a React Native project, first install Node.js, the runtime environment for JavaScript. Bundled with node is npm, so there is no need for a separate installation. Visit the official website to download Node.js, at www.nodejs.org.
The 2 most popular development environments for React Native is Expo and the React Native CLI. Expo is quicker and easier to work for beginners, but the React Native CLI offers more customization and control of the application. For more information, visit the official website. This post will cover Expo, since it is more simple. Once installed, navigate to a directory you wish to have the app in. Then run npx create-expo-app appName
, where appName
is the name of your application. It will start building the React Native app in the appName
folder it just created. Once it has finished building, run cd appName
to go into the folder. Run npx expo start
to run your application.
Expo allows developers to easily build a native app for iOS and Android with one codebase, using JavaScript and React; there is no need for native platform code (e.g. Swift for iOS, Java for Android). Developers can also access the native APIs of the device, such as the camera, location services, push notifications, etc. However, keep in mind that it does not support every device API, and you should consult documentation to confirm which APIs are available.
An emulator simulates what the app experience is like on that mobile platform. It is incredibly useful because it mimics the user experience of your application on that desired platform without having to purchase the physical device. iOS emulators can only be run on macOS devices, whereas Android emulators can be run on any device. Expo also has the Metro Bundler, which bundles the JavaScript into native code. It provides a QR code that you can scan on your mobile device that will run the app. Ensure you have the Expo Go app downloaded prior to scanning the QR code.
Components
React Native components are similar to React components; reusable, modular pieces of code. In React Native, there are core components, community components, and your own custom native components. Core components come with the React Native package, and translate into native components without native code. Community components need to be downloaded, and are developed by the community, but enables more app functionality. Custom native components need to be written in native code and integrated into the project, but should only be used for personal unqiue cases. Often times you can find libraries for all your components. The process to utilize components in React Native are just like React components; import the components into your desired file. Commonly used core components include View
, Text
, FlatList
, but there are numerous ones available. Components can be given styling information using the style
attribute. The style
attributes are similar to the ones found in HTML, but are slightly modified.
jsx
// Header.js
import { View, Text } from "react-native";
export default function HComponent() {
return (
<View>
<Text>Header Component</Text>
</View>
);
}
// App.js
import { View } from "react-native";
import HComponent from "./components/Header";
export default function App() {
return (
<View
style={{
flex: 1,
justifyContent: "flex-start",
padding: 30
}}>
<HComponent />
</View>
);
}
As the name suggests, the View
component is what the user sees on their device. It is the layout of the mobile screen, like a HTML div
element. The Text
component displays text similar to a HTML p
element. These are essential components to any React Native application.
ScrollView
is an important component in React Native, since it allows for scrolling of items on the device. It needs to be bounded by a height, so it is not an endless scrolling component. There are numerous paramters that can be passed in to adjust the ScrollView
, such as horizontal
, which makes the component scroll horizontally.
javascript
import { View, Text, ScrollView } from "react-native";
function ScrollComponent() {
return (
<View style={{flex: 1}}>
<ScrollView
horizontal={false}
style={{
paddingVertical: 50,
backgroundColor: "#fff"
}}>
<Text>Item 1</Text>
<Text>Item 2</Text>
<Text>Item 3</Text>
<Text>Item 4</Text>
</ScrollView>
</View>
);
}
Styling
Using inline styles for components may be convenient, but makes the code appear more messy. This is where StyleSheet
comes in; it extracts styles from components, and keeps all the styles together, similar to a CSS stylesheet. It is typically assigned to the styles
constant, and each object within styles
is named after the particular component it will be assigned to. Additionally, styles are now reusable if you want multiple components to have the same style.
javascript
import { StyleSheet } from 'react-native';
function App() {
return (
<View style={styles.container}>
<Text style={styles.title}>Title Component</Text>
</View>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'black',
},
title: {
fontSize: 35,
textAlign: 'center',
color: 'white',
}
})
Lists
When creating large lists in React Native, using the ScrollView
component is not ideal, since it has to render all of its child components before the app loads. This can lead to poor performance and slower rendering times for larger lists. For situations like this, the FlatList
component is useful due to its lazy rendering; it only renders the list items when they need to appear on the screen. It has data
and renderItem
as required props. data
is an array of items the list will take, while renderItem
is a function that determines how each item is rendered to the screen. keyExtractor
is a useful prop because it allows React to identify components to optimize rendering; it can cache items, and only re-render items if it has changed.
javascript
import { View, Text, FlatList } from 'react-native';
const data = [
{ id: 1, text: "Item 1" },
{ id: 2, text: "Item 2" },
{ id: 3, text: "Item 3" },
];
function renderItem({ item }){
return (
<View>
<Text>{item.text}</Text>
</View>
);
}
export default function App() {
return (
<View>
<FlatList
data={data}
renderItem={renderItem}
keyExtractor={(item) => item.id}
/>
</View>
);
}
SectionList
is another component that can be used to render lists with sections. It is similar to FlatList
, but enables separation of items in the list, instead of a long uninterrupted list. The required props are sections
which is an array of list sections, and renderItem
which renders the item. TherenderSectionHeader
prop identifies and renders the appropiate section header.
javascript
import { View, Text, SectionList } from 'react-native';
const data = [
{ title: 'Section 1',
data: [
'Item 1',
'Item 2',
'Item 3',
],
},
{ title: 'Section 2',
data: [
'Item 4',
'Item 5',
'Item 6',
],
},
];
export default function App() {
function renderItem({ item }){
return (
<View>
<Text>{item.text}</Text>
</View>
);
}
function renderSectionHeader({section: {title} }) {
return (
<Text>{title}</Text>
);
}
return (
<View>
<SectionList
sections={data}
renderItem={renderItem}
renderSectionHeader={renderSectionHeader}
/>
</View>
);
}
Input
TextInput
is a commonly used component for users to input text into the application via their native keyboard. The placeholder
prop is what displays in the input field prior to any input. keyboardType
determines the type of keyboard that pops up for the user, which for example, can be useful if you only want the user to input numbers. value
and onChangeText
can be used in combination with useState
to update a variable based on the contents of the input field. For more details on the TextInput
component, visit the offical documentation.
javascript
import { TextInput, View, Text } from 'react-native';
export default function Inputs() {
const [firstName, onChangeFirstName] = useState("");
const [lastName, onChangeLastName] = useState("");
const [phoneNumber, onChangePhoneNumber] = useState("");
return (
<View>
<Text>Below is some input fields.</Text>
<TextInput
value={firstName}
onChangeText={onChangeFirstName}
/>
<TextInput
value={lastName}
onChangeText={onChangeLastName}
/>
<TextInput
value={phoneNumber}
onChangeText={onChangePhoneNumber}
keyboardType="phone-pad"
/>
</View>
);
}
Pressable
is a core component in React Native that is wrapped around a child component, and then can detect the users press interactions on that component. The onPressIn
prop is called when the user presses on the element, and the onPressOut
prop is called when the user is no longer pressing on that element. onLongPress
is called when when the user holds on the pressable for more than 500ms, but the time interval can be changed with delayLongPress
. A common use case is for updating the local state of a variable, such as toggling the display of some content.
javascript
import { View, Text, Pressable } from 'react-native';
import { useState } from 'react';
export default function Menu() {
return (
<View>
<Pressable onPressIn={() => {
setShowMenu(!showMenu)
}}>
<Text>{showMenu : "Home" ? "Menu"}</Text>
</Pressable>
{!showMenu && (
<>
<Text>Showing the home screen</Text>
</>
)}
{showMenu && (
<>
<Text>Showing the menu screen</Text>
</>
)}
</View>
);
}
Images
Visual components such as images elevate the application. The Image
component allows you to render numerous types of images: from local project resources, external web images or even images from the user (e.g. camera roll). The mandatory source
prop determines the location of the image source that is rendered. The style
prop should be added to render the image appropiately.
javascript
import { View, Image, Text, StyleSheet } from 'react-native';
// import local image
import Logo from '../public/logo.png';
export default function RenderImage() {
return (
<View>
<Image source={Logo} style={styles.logo} />
<Text>The logo is rendered above</Text>
</View>
);
}
const styles = StyleSheet.create({
logo: {
height: 100,
width: 100,
}
})
There are numerous other props that can be passed into the Image
component. resizeMode
is a commonly used prop, that determines how the image is resized when the window frame does not match the image dimensions. To account for accessibility, set the accessible
prop to be true
and set accessibilityLabel
to a be description for the image. For more props and details, visit the official documentation.
ImageBackground
allows other components to be rendered ontop of the image. The source
prop is required to render the image, and inherits all the props from the Image
component.
Hooks
Just like in React, there are hooks in React Native that allow you to hook into state and features from other components. For a refresher on hooks, refer to this post.
useColorScheme
listens to color scheme updates from the users device. The values can be "light", "dark", or "null". A common convention is to name the constant colorScheme
.
javascript
import { View, Text, StyleSheet, useColorScheme } from 'react-native';
export default function Color() {
const colorScheme = useColorScheme();
return (
<View
style={[
styles.container,
colorScheme === 'light'
? {backgroundColor: '#fff'}
: {backgroundColor: '#333333'}
]}>
<Text>
The background color changes
</Text>
</View>
);
}
useWindowDimensions
extracts the current window or screen dimensions of the device and the users font scale preference. This information can be used to create a responsive layout and enable better formatting of the content based on the display it is being rendered on. Some layouts may work on larger screens but not on smaller screens, so the developer can create a separate layout for those users.
javascript
import { View, Text, StyleSheet, useWindowDimensions } from 'react-native';
function Window() {
const styles = getStyles();
return (
<>
<View style={styles.container}>
<Text>Some text in a box</Text>
</View>
</>
);
}
function getStyles() {
const {width, height, fontScale} = useWindowDimensions();
return StyleSheet.create({
container: {
height: height > 100 ? 100 : 50,
width: width > 100 ? 100 : 50,
}
})
}
export default Window;
export default
must be at the bottom of the fileThere are numerous other community hooks that can be used within your application from the React Native community. To install these community hooks, run npm install @react-native-community/hooks
. There are many useful hooks, such as useAppState
which keeps track if the application is in focus or in the background, useDeviceOrientation
which checks for if the user is in landscape or portrait mode, and numerous others. For more details, visit their GitHub page.
React Navigation
Navigation or routing is the process of moving between screens. React Navigation is a popular library used in React Native for customizable navigation. A stack navigator can be used to transition between various screens and manage the navigation history, so the user can go to the previous screen. To use React Navigation, first install the necessary dependencies. For more details, visit the official documentation. Assign the constant Stack
to createNativeStackNavigator()
. Then import and wrap the entire application around a NavigationContainer
component. Inside the component, wrap all screens in a Stack.Navigator
component. A prop to change the default screen is initialRouteName
, which can be assigned a screen. If each screen will have the same props, use the screenOptions
prop. It is recommened to hide each header, using headerShown: false
. For each new screen of your application, create a self-closing Stack.Screen
component, with props of name
and component
. name
takes in the name of the screen a string, and component
takes in the actual component screen.
javascript
import { NavigationContainer } from 'react-native';
import { createNativeStackNavigator } from '@react-navigation/native-stack';
import Screen1 from './components/Screen1';
import Screen2 from './components/Screen2';
const Stack = createNativeStackNavigator();
function App() {
return (
<NavigationContainer>
<Stack.Navigator
initialRouteName="Screen1"
screenOptions={{ headerShown: false }}
>
<Stack.Screen name="Screen1" component={Screen1} />
<Stack.Screen name="Screen2" component={Screen2} />
</Stack.Navigator>
</NavigationContainer>
);
}
To navigate to a specific screen, a component can utilize the navigation
prop. For example, a button
component can have its onPress
prop set to an anonymous or arrow function that sets navigation.navigate
to the screen name. navigation.push
navigates to the desired screen and adds it to the stack. The main difference between navigate
and push
is the latter only pushes the screen to the stack if it does not exist on it already. goBack
navigates to the previous screen in the navigation history, while popToTop
returns to the initial screen.
javascript
// navigate method
<Button onPress={() => navigation.navigate('Home')}>
Go to Home screen
</Button>
// push method
<Button onPress={() => navigation.push('Home')}>
Go to Home screen and add to navigation history stack
</Button>
// goBack method
<Button onPress={() => navigation.goBack()}>
Go to previous screen in navigation history stack
</Button>
// popToTop method
<Button onPress={() => navigation.popToTop()}>
Go to initial screen
</Button>