Loading...

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
certification

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;
Notice the different syntax; remember that hooks can only be called within functional components, so export default must be at the bottom of the file

There 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 navigationprop. 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>
© 2024 Sky Deng. All Rights Reserved.
Inspired by Takuya Matsuyama
3D Voxel Model: cmzw under CC License 4.0