DEV Community

Tamas Menyhart
Tamas Menyhart

Posted on

Pro tips for setting up a React Native project in 2020

Alt Text

Originally published at Medium

Introduction

The year is 2020 and mobile development is still in an upturn. For smaller businesses native mobile development is not an option most of the time because it requires a lot of time and developer resources so it is not cost effective enough. Luckily, React Native, Facebook's mobile cross platform development tool have improved a lot in the past years and it can be a viable option instead of pure native mobile development.

In this article I will discuss the following topics:

  • Motivation
  • Project bootstrapping
  • Initial directory and file changes
  • Explicit build tool and dependency versions
  • Code formatting and linting
  • Absolute imports and modularization
  • Grouped exports
  • Final thoughts

Motivation

Personally, I like to plan ahead, I'm convinced that if a project builds on a solid base then it will stand the test of time. Whenever I start working on a project, I always assume it is a long running one, meaning I try to prepare for whatever might happen but without being prophetic.

As at Supercharge I had the opportunity to successfully ship multiple React Native projects and support them for years in production, I would like to share with you some tips on how I would bootstrap a new React Native project nowadays.

Prerequisites

Other than the React Native requirements, I recommend installing Node Version Manager (MacOS, Linux, Windows), NodeJS 8.17.0 or newer, Node Package Manager 5.2 or newer, and Visual Studio Code with ESLint and Prettier extensions.

Project bootstrapping

I always use the React Native CLI to create a new React Native project, because in the long term there's a great chance that I have to write some native (Android, IOS) code.

So let's create our new React Native project called MyApp:

npx react-native init MyApp --template react-native-template-typescript --npm
Enter fullscreen mode Exit fullscreen mode
  • NPX is a package runner for NPM, and it's available since NPM 5.2. With NPX there's no need to install the 'react-native-cli' package globally just to initialize our project.
  • The '--template react-native-template-typescript' parameter will use the Typescript template to create the project. I always use Typescript for static type checking, it will make things more developer friendly and less prone to errors.
  • The '--npm' parameter will make the project use NPM instead of Yarn to install initial dependencies.

Initial directory and file changes

I like to move all of the project javascript source files to an 'src' directory to keep the project root nice and tidy. Then I move the 'App.tsx' file from the project root to the 'src' directory. Finally, because we're using Typescript in the project, I rename the 'index.js' file to 'index.ts'.

Explicit build tool and dependency versions

It's important to make project builds repeatable, no matter where the project is built (CI, other developer's machine). That's why I like to use explicit build tools, package manager and dependency versions wherever possible.

To use explicit Node and NPM versions, some changes need to be made:

Update package.json file with 'engines' block:

"engines": {
  "node:": "x.y.z",
  "npm": "a.b.c",
}
Enter fullscreen mode Exit fullscreen mode
  • Where "x.y.z" is the explicit Node version you want to use (Eg: "12.16.1")
  • Where "a.b.c" is the explicit NPM version you want to use (Eg: "6.13.4")

Create a .npmrc file in the project root directory with the following content:

engine-strict=true
save-exact=true
Enter fullscreen mode Exit fullscreen mode
  • The 'engine-strict=true' line will make NPM look for allowed version defined in the "engines" block when I am trying to install dependencies
  • The 'save-exact=true' line will make NPM install explicit dependency versions. You're correct to think that's why we have the 'package-lock.json' file, but I like to see explicit dependency versions in the package.json file as well.

Code formatting and linting

The consistent code style is really important when multiple developers contribute to the project. To make code formatting become nothing to worry about, I use Prettier, which takes the burden off my shoulders.

There's a '.prettierrc.js' file in the project root already, but we can customize it further. In my case I use the following Prettier config:

module.exports = {
  tabWidth: 2,
  useTabs: false,
  printWidth: 140,
  semi: true,
  trailingComma: 'es5',
  bracketSpacing: true,
  arrowParens: 'always',
  singleQuote: true,
  jsxSingleQuote: true,
  jsxBracketSameLine: true,
};
Enter fullscreen mode Exit fullscreen mode

To make code formatting an automatic task on save, I populate the workspace config file of Visual Studio Code (.vscode/settings.json) with the following content:

{
  "editor.tabSize": 2,
  "editor.formatOnSave": false,
  "[javascript]": {
    "editor.formatOnSave": true
  },
  "[typescript]": {
    "editor.formatOnSave": true
  },
  "[javascriptreact]": {
    "editor.formatOnSave": true
  },
  "[typescriptreact]": {
    "editor.formatOnSave": true
  },
  "typescript.tsdk": "node_modules/typescript/lib"
}
Enter fullscreen mode Exit fullscreen mode

As I prefer single quotes in the source files, I add rule override to the '.eslintrc.js' file to not to warn me about single quotes:

module.exports = {
  root: true,
  extends: '@react-native-community',
  parser: '@typescript-eslint/parser',
  plugins: ['@typescript-eslint'],
  overrides: [
    {
      files: ['*.js', '*.ts', '*.jsx', '*.tsx'],
      rules: {
        'jsx-quotes': 0,
      },
    },
  ],
};
Enter fullscreen mode Exit fullscreen mode

As a final step, I add linting scripts to the package.json file:

"scripts": {
  "lint:style": "prettier --check 'src/**/**.{js,ts,jsx,tsx}'",
  "lint:code": "eslint src/ --ext .js,.jsx,.ts,.tsx",
  "lint": "npm run lint:style && npm run lint:code",
  "test": "jest",
  "sanity": "npm run lint && npm run test"
}
Enter fullscreen mode Exit fullscreen mode

Absolute imports and modularization

I don't like long relative imports when I'm deep in the directory tree. It makes refactoring circumstantial. In order to avoid this, I always modularize the codebase based on some logic and use absolute imports in the source files.

First, let's create the following directory and file structure:

src/
  app/
    App.tsx
    package.json
  core/
    package.json
  ui/
    package.json
Enter fullscreen mode Exit fullscreen mode

By creating package json files in the sub directories, I define sub-modules. Let's populate the contents of the package.json files:

src/app/package.json

{
  "name": "app"
}
Enter fullscreen mode Exit fullscreen mode

src/core/package.json

{
  "name": "core"
}
Enter fullscreen mode Exit fullscreen mode

src/ui/package.json

{
  "name": "ui"
}
Enter fullscreen mode Exit fullscreen mode

To be able to use absolute imports, I need to update the tsconfig.json file in the project root:

{
  "compilerOptions": {
    "baseUrl": "./src",
  }
}
Enter fullscreen mode Exit fullscreen mode

After these changes we can use import statements the following way:

src/app/App.tsx

import { Row } from 'ui/component/layout/Row.component.tsx'
import { someExportedUtilMethod } from 'core/util/utils.ts'
Enter fullscreen mode Exit fullscreen mode

To avoid circular imports and keep the separation of concerns intact, there are some important rules to pay attention to:

  • Files under any sub-module directory should use relative imports when importing from files under the same sub-module directory
  • Files under the 'app' directory should import from files under the 'app', 'core' and 'ui' directories
  • Files under the 'core' directory should only import from files under the 'core' directory
  • Files under the 'ui' directory should import from files under the 'ui' and 'core' directories

Grouped exports

When named exports are used instead of default exports, they can be grouped in index files which then can be used to import the named exports from. The advantage of this is that files can be moved freely under a given root directory and there is no need to update every single source file where these files are used to import from.

First, I have the following directory and file structure:

src/
  app/
    App.tsx
    package.json
  core/
    package.json
  ui/
    component/
      button/
        index.ts
        FlatButton.component.tsx
        RaisedButton.component.tsx
      layout/
        index.ts
        Column.component.tsx
        Row.component.tsx
      index.ts
    package.json
Enter fullscreen mode Exit fullscreen mode

src/ui/component/button/index.ts

export * from './FlatButton.component';
export * from './RaisedButton.component';
Enter fullscreen mode Exit fullscreen mode

src/ui/component/layout/index.ts

export * from './Column.component';
export * from './Row.component';
Enter fullscreen mode Exit fullscreen mode

src/ui/component/index.ts

export * from './button';
export * from './layout';
Enter fullscreen mode Exit fullscreen mode

After I've populated the index files, I can use imports the following way:

src/app/App.tsx

import { Column, FlatButton, RaisedButton, Row } from 'ui/component';
Enter fullscreen mode Exit fullscreen mode

This way I don't have to explicitly define the path for the import, I can move the components under the component directory freely, I only have to update the related index files.

Final thoughts

I hope this bootstrapping guide will be helpful for you in the future when starting a new React Native project.

Remember, nothing is written in stone, these are just suggestions based on my own experience with React Native.

Thanks for reading it through, I welcome any feedback regarding this topic.


At Supercharge, we are a next-generation innovation agency working with our clients to create transformative digital solutions. If you liked this article, check out some of Supercharge's other articles on our blog, or follow us on LinkedIn, and Facebook. If you're interested in open positions, follow this link.

Top comments (1)

Collapse
 
merlier profile image
Merlier

About "Absolute imports and modularization", it's a cool thing. You can use also the NPM module babel-plugin-module-resolver to have absolute imports without creating too many package.json