Creating a New Application page

Generate a new React Native application using the CLI.

Overview

In this section, you will:

  • Use the React Native CLI to generate a new React Native application.
  • Test code with React Testing Library.
  • Add code quality tools.

Objective 1: Generate the “Place My Order” application

Create a new React Native application named “Place My Order” that supports TypeScript and can be emulated and tested.

A mobile phone displaying the root page of the newly created application served by the dev server. The page displays a 'Welcome to React Native' header and some miscellaneous instructions that we will be disregarding.
Screen capture of the completed exercise.

Using the React Native CLI

React Native has a built-in command line interface. Rather than install and manage a specific version of the CLI globally, we recommend you access the current version at runtime using npx, which ships as part of npm. With npx react-native <command>, the current stable version of the CLI will be downloaded and executed at the time the command is run.

Setup 1

✏️ Open a terminal and move to a location where you want to generate your React application, then execute the following command:

npx react-native@0.74.1 init PlaceMyOrder --version 0.74.1

The command runs npx, using react-native to create a new React Native application named "PlaceMyOrder with TypeScript support.

✏️ Now cd into the new directory created for the application:

cd PlaceMyOrder

When cloning a project from a git repository, it will also be necessary to run npm install in the directory, but the app setup has done this for us already.

Verify 1

Once you have completed the previous step you will be able to start the development server and see the default welcome page.

The development server is a useful tool. When it starts, it transpiles the TypeScript and JSX code into JavaScript and bundles it for delivery to the device. It also watches for changes to the source code, and when they occur, it repeats the process, then causes the device to reload with those changes.

✏️ Start the virtual device you created in the previous module.

✏️ Run the command:

npm run start

✏️ Press the a key to run the app on the open Android virtual device.

The first time you run it for a given target will take significantly longer than subsequent runs. Once open, you should see the default "Welcome to React Native" screen.

Objective 2: Add testing infrastructure

A mobile phone displaying the root page of the newly created application served by the dev server. The page displays a “Place My Order: Coming Soon!” message.

Testing code with React Testing Library

We will create unit tests for our application using the React Native Testing Library. The unit tests for components will be focused on verifying that a component creates the correct output based on the state the component is provided. We also need to create tests for any React custom Hooks we create to ensure they provide the expected results.

Setup 2

✏️ Install the new dev dependencies:

npm install --save-dev @testing-library/react-native@12 @types/jest@29

✏️ Create jest-setup.ts and update it to be:

import '@testing-library/react-native/extend-expect';

✏️ Update jest.config.js to be:

module.exports = {
  preset: 'react-native',
  setupFilesAfterEnv: ['<rootDir>/jest-setup.ts'],
};

✏️ Delete the unneeded generated folder __tests__.

✏️ Create App.test.tsx and update it to be:

import {render, screen} from '@testing-library/react-native';
import App from './App';

describe('App', () => {
  it('renders', async () => {
    render(<App />);
    expect(screen.getByText(/Place my order/i)).toBeOnTheScreen();
  });
});

✏️ Update App.tsx to be:

import {Text} from 'react-native';

const App: React.FC = () => {
  return (
    <Text>Place My Order: Coming Soon!</Text>
  );
}

export default App;

Verify 2

After completing the previous steps you will be able to run unit tests. Execute the command:

npm run test

and "Test Suites: 1 passed, 1 total" will be written to the console.

Objective 3: Clean up the generated code and add helpers

Before we begin adding any content, it’s a good idea to clean up generated files and add code quality tools.

Setup 3

✏️ Install the new dev dependencies:

npm install --save-dev @bitovi/eslint-config@1 depcheck@1

✏️ Create .depcheckrc and update it to be:

ignores:
  - "@types/*"
  - "@babel/*"
  - "babel-*"
  - "*-babel"
  - "@react-native/eslint-config"
  - identity-obj-proxy
  - react-native-dotenv

✏️ Update .eslintrc.js to be:

module.exports = {
  root: true,
  extends: "@bitovi/eslint-config/react",
  settings: {
    "import/ignore": ["react-native/*"],
  },
  rules: {
    "@typescript-eslint/consistent-type-imports": [
      "error",
      {
        prefer: "no-type-imports",
        fixStyle: "inline-type-imports",
      },
    ],
  },
}

✏️ Update .gitignore to be:

# OSX
#
.DS_Store

# Windows
#
Thumbs.db

# Xcode
#
build/
*.pbxuser
!default.pbxuser
*.mode1v3
!default.mode1v3
*.mode2v3
!default.mode2v3
*.perspectivev3
!default.perspectivev3
xcuserdata
*.xccheckout
*.moved-aside
DerivedData
*.hmap
*.ipa
*.xcuserstate
**/.xcode.env.local

# Android/IntelliJ
#
build/
.idea
.gradle
local.properties
*.iml
*.hprof
.cxx/
*.keystore
!debug.keystore

# node.js
#
node_modules/
npm-debug.log
yarn-error.log

# fastlane
#
# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the
# screenshots whenever they are needed.
# For more information about the recommended setup visit:
# https://docs.fastlane.tools/best-practices/source-control/

**/fastlane/report.xml
**/fastlane/Preview.html
**/fastlane/screenshots
**/fastlane/test_output

# Bundle artifact
*.jsbundle
*.tsbuildinfo

# Ruby / CocoaPods
**/Pods/
/vendor/bundle/

# Temporary files created by Metro to check the health of the file watcher
.metro-health-check*

# testing
/coverage

# Yarn
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

✏️ Update .prettierrc.js to be:

module.exports = {
  endOfLine: "auto",
  semi: false,
  trailingComma: "all",
}

✏️ Update metro.config.js to be:

// eslint-disable-next-line @typescript-eslint/no-var-requires
const { getDefaultConfig, mergeConfig } = require("@react-native/metro-config")

/**
 * Metro configuration
 * https://facebook.github.io/metro/docs/configuration
 *
 * @type {import('metro-config').MetroConfig}
 */
const config = {}

module.exports = mergeConfig(getDefaultConfig(__dirname), config)

✏️ Update package.json to be:

{
  "name": "PlaceMyOrder",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "clean": "rm -rf tsconfig.tsbuildinfo coverage android/.gradle android/build android/app/build node_modules/.cache",
    "depcheck": "depcheck .",
    "eslint": "eslint .",
    "eslint:fix": "eslint --fix .",
    "ios": "react-native run-ios",
    "lint": "npm run eslint && npm run prettier",
    "lint:fix": "npm run eslint:fix && npm run prettier:fix",
    "precheck": "npm run typecheck && npm run lint && npm run depcheck && npm test",
    "prettier": "prettier --check .",
    "prettier:fix": "prettier --write .",
    "start": "react-native start --reset-cache",
    "test": "jest",
    "test:inspect": "node --inspect-brk ./node_modules/.bin/jest --watch",
    "test:watch": "jest --watch",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "react": "18.2.0",
    "react-native": "0.74.1"
  },
  "devDependencies": {
    "@babel/core": "^7.20.0",
    "@babel/preset-env": "^7.20.0",
    "@babel/runtime": "^7.20.0",
    "@bitovi/eslint-config": "^1.8.0",
    "@react-native/babel-preset": "0.74.83",
    "@react-native/eslint-config": "0.74.83",
    "@react-native/metro-config": "0.74.83",
    "@react-native/typescript-config": "0.74.83",
    "@testing-library/react-native": "^12.5.0",
    "@types/jest": "^29.5.12",
    "@types/react": "^18.2.6",
    "@types/react-test-renderer": "^18.0.0",
    "babel-jest": "^29.6.3",
    "depcheck": "^1.4.7",
    "eslint": "^8.19.0",
    "jest": "^29.6.3",
    "prettier": "2.8.8",
    "react-test-renderer": "18.2.0",
    "typescript": "5.0.4"
  },
  "engines": {
    "node": ">=20"
  }
}

✏️ Update tsconfig.json to be:

{
  "extends": "@react-native/typescript-config/tsconfig.json",
  "compilerOptions": {
    "module": "ES2022",
    "types": ["react-native", "jest", "node"],

    "declaration": true,
    "composite": true,
    "incremental": true,
    "strict": true,
    "noEmit": true,

    "allowJs": false,
    "allowSyntheticDefaultImports": true,
    "downlevelIteration": true,
    "esModuleInterop": true,
    "forceConsistentCasingInFileNames": true,
    "isolatedModules": true,
    "noFallthroughCasesInSwitch": true,
    "noImplicitReturns": true,
    "noUnusedLocals": true,
    "skipLibCheck": true,
    "useDefineForClassFields": true
  },
  "include": ["@types", "*.ts", "*.tsx", "src"],
  "exclude": ["node_modules"]
}

✏️ Now that we have prettier and eslint set up, we need to make sure our code is following the new standards. You can apply most of the rules automatically. Run:

npm run lint:fix

Note: If you installed the ESLint and Prettier VS Code plugins and enable "format on save" in the settings, VS Code will make the necessary changes any time you save a file too!

Verify 3

✏️ Run the new code quality scripts.

npm run typecheck
npm run eslint
npm run prettier
npm run depcheck

These scripts should all pass successfully. You can run all of these, and the tests, with

npm run precheck

Next steps

Next, let’s learn about JSX to understand React’s templating language.