react native
Jun 06 2019 02:44pm | | Share

Custom React Native Configurations – a simple solution

By Rod Nolan

The problem: you need to be able to create a compiled version of your React Native app that differs across a few target environments: development, staging, production.

 

I experimented with whatever libraries I could find and realized that they were either feature poor or required too much work for my liking. That experimentation phase led me to discover that the solution to this problem is quite simple: it comes down to a couple of custom scripts in package.json. You create separate configuration files for each target environment and use a few scripts to copy the desired config file into a pre-determined location before compiling the project. Building the parts of the solution is easy. Also, because there are no additional dependencies, on-boarding new team members is simple.

 

If you’d like to follow along, you should start with a fresh project.

 

~/$ react-native init envVars

~/$ cd envVars

~/$ react-native run-ios | run-android

 

Note: In the samples throughout this article, option1 | option2 means that you should use option1 or option 2 but not both.

 

The first step is to define the variables that change from environment to environment. These files contain two important sections:

  1. the comments section informs other developers how to edit the values if new requirements come up in the future.
  2. the code defines the values that differ from one environment to the next, the ones you’ll use throughout your application. In this example, there are three configurations: dev, stage, prod. In each we’re only defining a single variable but you can add as many variables as you need in your solution.

 

{project_root}/env.dev.js

/**

 * ./config/index.js is generated. DO NOT EDIT THIS FILE! 

 * to update configuration values, edit the appropriate file:

 *    ./.env.dev

 *    ./.env.stage

 *    ./.env.prod

 * and run the corresponding script

 *    `yarn prep-dev`

 *    `yarn prep-stage`

 *    `yarn prep-prod`

 */

module.exports = {

  ENV_NAME: 'dev',

};

 

{project_root}/env.stage.js

/**

 * ./config/index.js is generated. DO NOT EDIT THIS FILE! 

 * to update configuration values, edit the appropriate file:

 *    ./.env.dev

 *    ./.env.stage

 *    ./.env.prod

 * and run the corresponding script

 *    `yarn prep-dev`

 *    `yarn prep-stage`

 *    `yarn prep-prod`

 */

module.exports = {

  ENV_NAME: 'staging',

};

 

{project_root}/env.prod.js

/**

 * ./config/index.js is generated. DO NOT EDIT THIS FILE! 

 * to update configuration values, edit the appropriate file:

 *    ./.env.dev

 *    ./.env.stage

 *    ./.env.prod

 * and run the corresponding script

 *    `yarn prep-dev`

 *    `yarn prep-stage`

 *    `yarn prep-prod`

 */

module.exports = {

  ENV_NAME: 'production',

};

 

You create a file for each environment that you intend to compile for. In most cases development, staging and production will be enough but this solution is infinitely scalable. Also, you should commit these files to your source control repository. So now that you’ve defined the variables for each of your target environments, you need a way to decide which set of variables to use for your build.

 

The next step is to set up a few simple scripts in package.json that allow you to quickly copy the file that contains the desired environment into a location that you will use throughout the rest of your application. These scripts are simple enough to define inline, right in package.json. They look like this:

 

{project_root}/package.json

  "scripts": {

    "start": "node node_modules/react-native/local-cli/cli.js start",

    "prep-dev": "echo \"Applying development config\" && cp .env.dev.js config/env.js",

    "prep-stage": "echo \"Applying staging config\" && cp .env.stage.js config/env.js",

    "prep-prod": "echo \"Applying production config\" && cp .env.prod.js config/env.js"

    …

  },

 

Note that each of the prep-* scripts simply copies the appropriate source file into the same location in the project, overwriting whatever was there. Since `config/env.js` is generated on demand at compile time and is prone to frequent changes by each developer at compile time, this file should be kept out of the source control repository. If you’re using git, add the following lines to the repo’s .gitignore file.

 

{project_root}/.gitignore

# generated config files

config/

 

These new scripts can be called at will, but you must remember to include this new step in your compilation process. What used to be:

 

~/$ cd envVars

~/$ yarn start

~/$ react-native run-ios | run-android

 

must now be:

 

~/$ cd envVars

~/$ yarn prep-dev | yarn prep-stage | yarn prep-prod

~/$ yarn start

~/$ react-native run-ios | run-android

 

To aid in breaking old habits and developing the habit of calling your new prep-* scripts, you can add some optional scripts that look like this:

 

{project_root}/package.json

  "scripts": {

    "start": "echo \"Please call `yarn start-dev`, `yarn start-stage`, or `yarn start-prod`\"",

    "start-dev": "yarn prep-dev && yarn start-step-two",

    "start-stage": "yarn prep-stage && yarn start-step-two",

    "start-prod": "yarn prep-prod && yarn start-step-two",

    "start-step-two": "node node_modules/react-native/local-cli/cli.js start",

    "prep-dev": "echo \"Applying development config\" && cp .env.dev.js config/env.js",

    "prep-stage": "echo \"Applying staging config\" && cp .env.stage.js config/env.js",

    "prep-prod": "echo \"Applying production config\" && cp .env.prod.js config/env.js"

    …

  },

 

This modification simply combines two steps into one, helping you to make a small adjustment to a well-established work flow (aka: old habits that are hard to break). Now your start-up routine looks like this:

 

~/$ cd envVars

~/$ yarn start-dev | start-stage | start-prod

~/$ react-native run-ios | run-android

 

The final step is to import and use the environment variables anywhere you need them throughout your application.

 

{project_root}/App.js

import React, {Component} from 'react';

import {Text, View} from 'react-native';

 

// import environment vars 

import env from './config/env';

 

export default class App extends Component<Props> {

  render() {

    return (

      <View>

 {/* and use them as required */}

        <Text>Built for {env.ENV_NAME}</Text>

      </View>

    );

  }

}

 

Once your project is up and running in the simulator, simply reload to see the changes.

 

iphone production

 

If you’d like to avoid manually typing code, you can grab the patches from here: https://github.com/DEV6hub/DEV6Blog/tree/master/react-native-env-config.

 

Happy building!