Définition de la variable d'environnement dans React-native?

152

J'utilise react-native pour créer une application multiplateforme, mais je ne sais pas comment définir la variable d'environnement pour pouvoir avoir différentes constantes pour différents environnements.

Exemple:

development: 
  BASE_URL: '',
  API_KEY: '',
staging: 
  BASE_URL: '',
  API_KEY: '',
production:
  BASE_URL: '',
  API_KEY: '',
Damon Yuan
la source
vous pouvez essayer ceciimport {Platform} from 'react-native'; console.log(Platform);
Praveen Prasad

Réponses:

138

Au lieu de coder en dur les constantes de votre application et d'activer l'environnement (j'expliquerai comment le faire dans un instant), je suggère d'utiliser la suggestion à douze facteurs de définir votre processus de construction BASE_URLet le vôtre API_KEY.

Pour savoir comment exposer votre environnement react-native, je suggère d'utiliser babel-plugin-transform-inline-environment-variables de Babel .

Pour que cela fonctionne, vous devez télécharger le plugin, puis vous devrez configurer un .babelrcet cela devrait ressembler à ceci:

{
  "presets": ["react-native"],
  "plugins": [
    "transform-inline-environment-variables"
  ]
}

Et donc, si vous transpilez votre code natif de réaction en exécutant API_KEY=my-app-id react-native bundle(ou start, run-ios ou run-android), tout ce que vous avez à faire est de faire ressembler votre code à ceci:

const apiKey = process.env['API_KEY'];

Et puis Babel remplacera cela par:

const apiKey = 'my-app-id';

J'espère que cela t'aides!

chapinkapa
la source
7
Cela semble être une excellente solution, mais ne fonctionne pas pour moi à [email protected]. La seule propriété sur process.envest NODE_ENV.
Adam Faryna
2
Voir la réponse ci-dessous par Jack Zheng ... vous ne pouvez pas accéder à la variable via process.env.API_KEY... utilisez à la process.env['API_KEY']place
Steven Yap
6
J'obtiens le process.env ['API_KEY'] comme non défini. Quelqu'un peut-il m'aider à configurer cela
user1106888
2
J'ai eu le même problème: undefined
Guto Marrara Marzagao
7
Fonctionne pour moi dans la v0.56. Vous devez vider le cache du bundler en exécutant react-native start --reset-cachechaque fois que vous modifiez les variables d'environnement.
soheilpro
55

La solution la plus simple (ni la meilleure ni l' idéale ) que j'ai trouvée était d'utiliser react-native-dotenv . Vous ajoutez simplement le préréglage "react-native-dotenv" à votre .babelrcfichier à la racine du projet comme ceci:

{
  "presets": ["react-native", "react-native-dotenv"]
}

Créez un .envfichier et ajoutez des propriétés:

echo "SOMETHING=anything" > .env

Puis dans votre projet (JS):

import { SOMETHING } from 'react-native-dotenv'
console.log(SOMETHING) // "anything"
Slavo Vojacek
la source
1
J'espérais une solution basée sur .env. Je vous remercie!
Anshul Koka
3
@Slavo Vojacek Comment puis-je l'utiliser pour configurer par exemple un base_urlpour les deux staginget production?
Compaq LE2202x
@ CompaqLE2202x Je ne suis pas sûr de comprendre? Vous demandez-vous d'utiliser différents .envfichiers (par environnement) ou de réutiliser certaines de vos valeurs dans différents .envfichiers, afin de ne pas les dupliquer, par exemple, entre la mise en scène et la production?
Slavo Vojacek
5
@SlavoVojacek Je pose des questions sur différents .envfichiers par environnement, disons staginget production.
Compaq LE2202x
@SlavoVojacek ne pourriez-vous pas écraser les valeurs dans une étape CI ou lors du déploiement?
mgamsjager
37

À mon avis, la meilleure option est d'utiliser react-native-config . Il prend en charge 12 facteurs .

J'ai trouvé ce package extrêmement utile. Vous pouvez définir plusieurs environnements, par exemple le développement, la mise en scène, la production.

Dans le cas d'Android, les variables sont également disponibles dans les classes Java, gradle, AndroidManifest.xml etc. Dans le cas d'iOS, les variables sont également disponibles dans les classes Obj-C, Info.plist.

Vous créez simplement des fichiers comme

  • .env.development
  • .env.staging
  • .env.production

Vous remplissez ces fichiers avec des clés, des valeurs telles que

API_URL=https://myapi.com
GOOGLE_MAPS_API_KEY=abcdefgh

puis utilisez-le simplement:

import Config from 'react-native-config'

Config.API_URL  // 'https://myapi.com'
Config.GOOGLE_MAPS_API_KEY  // 'abcdefgh'

Si vous souhaitez utiliser différents environnements, vous définissez essentiellement la variable ENVFILE comme ceci:

ENVFILE=.env.staging react-native run-android

ou pour assembler l'application pour la production (android dans mon cas):

cd android && ENVFILE=.env.production ./gradlew assembleRelease
Patrik Prevuznak
la source
9
Il peut être intéressant de noter que dans le README il indique Gardez à l'esprit que ce module n'obscurcit pas ou ne crypte pas les secrets pour l'empaquetage, donc ne stockez pas les clés sensibles dans .env. Il est fondamentalement impossible d'empêcher les utilisateurs de procéder à la rétro-ingénierie des secrets des applications mobiles, alors concevez votre application (et vos API) en gardant cela à l'esprit
Marklar
Le problème est que cela ne fonctionnera pas avec certains frameworks comme Twitter, qui nécessitent de définir leur clé comme com.twitter.sdk.android.CONSUMER_KEY dans votre .env
thibaut noah
Si vous voulez mettre la clé dans le manifeste, l'extension le prend en charge. Ce n'est tout simplement pas décrit dans cette réponse. Vous pouvez utiliser les variables dans les fichiers XML, Java et JS.
sfratini
4
react-native-config ne fonctionne pas avec RN 0.56, il a des problèmes non résolus et il n'est pas maintenu pendant plus de 6 mois. Le problème qui tue son utilisation dans RN est github.com/luggit/react-native-config/issues/267 , voici un piratage pour le faire fonctionner github.com/luggit/react-native-config/issues/285
Marecky
24

React native n'a pas le concept de variables globales. Il applique strictement la portée modulaire , afin de promouvoir la modularité et la réutilisabilité des composants.

Parfois, cependant, vous avez besoin que les composants soient conscients de leur environnement. Dans ce cas, il est très simple de définir un Environmentmodule que les composants peuvent ensuite appeler pour obtenir des variables d'environnement, par exemple:

environment.js

var _Environments = {
    production:  {BASE_URL: '', API_KEY: ''},
    staging:     {BASE_URL: '', API_KEY: ''},
    development: {BASE_URL: '', API_KEY: ''},
}

function getEnvironment() {
    // Insert logic here to get the current platform (e.g. staging, production, etc)
    var platform = getPlatform()

    // ...now return the correct environment
    return _Environments[platform]
}

var Environment = getEnvironment()
module.exports = Environment

mon-composant.js

var Environment = require('./environment.js')

...somewhere in your code...
var url = Environment.BASE_URL

Cela crée un environnement singleton auquel vous pouvez accéder de n'importe où dans la portée de votre application. Vous devez explicitement require(...)le module de tous les composants qui utilisent des variables d'environnement, mais c'est une bonne chose.

tohster
la source
19
mon problème est de savoir comment getPlatform(). J'ai créé un fichier comme celui-ci mais je ne peux pas terminer la logique ici dans React Native
Damon Yuan
@DamonYuan cela dépend entièrement de la façon dont vous configurez vos packages. Je n'ai aucune idée de ce que je veux dire stagingou productionmême parce que cela dépend de votre environnement. Par exemple, si vous voulez différentes saveurs pour IOS vs Android, vous pouvez initialiser l' environnement en importer vos index.ios.jset des index.android.jsfichiers et établir la plate - forme là - bas, par exemple Environment.initialize('android').
tohster
@DamonYuan fait ce que je mets de l'aide, ou avez-vous besoin de plus de précisions?
chapinkapa le
C'est très agréable lorsque vous contrôlez le code.
J'exécute
2
Si vous créez un env.jsfichier, assurez-vous de l'ignorer lors de l'archivage dans le référentiel et copiez les clés utilisées, avec des valeurs de chaîne vides, dans un autre env.js.examplefichier que vous archivez afin que les autres puissent créer votre application plus facilement. Si vous archivez accidentellement des secrets de projet, envisagez de réécrire l'historique pour les supprimer non seulement de la source, mais aussi de son historique.
Josh Habdas
17

J'ai utilisé le __DEV__polyfill intégré à react-native pour résoudre ce problème. Il est automatiquement défini sur truetant que vous ne créez pas de réaction native pour la production.

Par exemple:

//vars.js

let url, publicKey;
if (__DEV__) {
  url = ...
  publicKey = ...
} else {
  url = ...
  publicKey = ...
}

export {url, publicKey}

Alors juste import {url} from '../vars'et vous obtiendrez toujours le bon. Malheureusement, cela ne fonctionnera pas si vous voulez plus de deux environnements, mais c'est facile et n'implique pas l'ajout de dépendances supplémentaires à votre projet.

Logister
la source
connaissez-vous un moyen de "forcer" DEV à TRUE même lors de la création d'une version de version dans xcode?
realtebo
1
Nan. Je viens de commenter les variables prod, puis copiez-collez les variables dev dans la section prod lorsque je veux faire une version de version avec des variables de développement.
Journal du
1
J'ai trouvé la solution la plus élégante
Dani Sh90
5

La méthode spécifique utilisée pour définir les variables d'environnement varie selon le service CI, l'approche de construction, la plate-forme et les outils que vous utilisez.

Si vous utilisez Buddybuild for CI pour créer une application et gérer des variables d'environnement , et que vous avez besoin d'accéder à la configuration à partir de JS, créez un env.js.exampleavec des clés (avec des valeurs de chaîne vides) pour l'enregistrement dans le contrôle de code source, et utilisez Buddybuild pour produire un env.jsfichier au moment de la construction dans l' post-cloneétape, en masquant le contenu du fichier dans les journaux de construction, comme ceci:

#!/usr/bin/env bash

ENVJS_FILE="$BUDDYBUILD_WORKSPACE/env.js"

# Echo what's happening to the build logs
echo Creating environment config file

# Create `env.js` file in project root
touch $ENVJS_FILE

# Write environment config to file, hiding from build logs
tee $ENVJS_FILE > /dev/null <<EOF
module.exports = {
  AUTH0_CLIENT_ID: '$AUTH0_CLIENT_ID',
  AUTH0_DOMAIN: '$AUTH0_DOMAIN'
}
EOF

Astuce: Ne pas oublier d'ajouter env.jsà .gitignoresi config et les secrets ne sont pas vérifiées dans le contrôle source accidentellement au cours du développement.

Vous pouvez ensuite gérer la façon dont le fichier est écrit en utilisant les variables Buddybuild comme BUDDYBUILD_VARIANTS, par exemple, pour obtenir un meilleur contrôle sur la façon dont votre configuration est produite au moment de la construction.

Josh Habdas
la source
dans l'ensemble, j'aime l'idée, mais comment fonctionne la env.js.examplepièce? disons que je veux lancer l'application dans mon environnement local. si mon env.jsfichier est dans gitignore et env.js.exampleest utilisé comme un aperçu, le env.js.examplen'est pas une extension JS légitime, donc je suis juste un peu confus sur ce que vous entendez par cette partie
volk
@volk Le env.js.examplefichier se trouve dans la base de code en tant que document de référence, une source canonique de vérité sur les clés de configuration que l'application souhaite utiliser. Il décrit à la fois les clés nécessaires pour exécuter l'application, ainsi que le nom de fichier attendu une fois copié et renommé. Le modèle est courant dans les applications Ruby utilisant la gemme dotenv , d'où j'ai tiré le modèle.
Josh Habdas le
3

Je pense que quelque chose comme la bibliothèque suivante pourrait vous aider à résoudre le morceau manquant du puzzle, la fonction getPlatform ().

https://github.com/joeferraro/react-native-env

const EnvironmentManager = require('react-native-env');

// read an environment variable from React Native
EnvironmentManager.get('SOME_VARIABLE')
  .then(val => {
    console.log('value of SOME_VARIABLE is: ', val);

  })
  .catch(err => {
    console.error('womp womp: ', err.message);
  });

Le seul problème que je vois avec cela, c'est que c'est du code asynchrone. Il existe une demande d'extraction pour prendre en charge getSync. Vérifiez-le aussi.

https://github.com/joeferraro/react-native-env/pull/9

leonfs
la source
3
Approuvé pour avoir fourni une approche alternative non mentionnée. Aucune taille ne convient à tous.
Josh Habdas
La demande d'extraction asynchrone a été fusionnée dans
jcollum
5
react-native-env ne semble pas prendre en charge Android. À quoi ça sert?
jcollum
3

J'ai créé un script de pré-construction pour le même problème car j'ai besoin de différents points de terminaison API pour les différents environnements

const fs = require('fs')

let endPoint

if (process.env.MY_ENV === 'dev') {
  endPoint = 'http://my-api-dev/api/v1'
} else if (process.env.MY_ENV === 'test') {
  endPoint = 'http://127.0.0.1:7001'
} else {
  endPoint = 'http://my-api-pro/api/v1'
}

let template = `
export default {
  API_URL: '${endPoint}',
  DEVICE_FINGERPRINT: Math.random().toString(36).slice(2)
}
`

fs.writeFile('./src/constants/config.js', template, function (err) {
  if (err) {
    return console.log(err)
  }

  console.log('Configuration file has generated')
})

Et j'ai créé une personnalisation npm run scriptspour exécuter une exécution native de réaction.

Mon package-json

"scripts": {
    "start-ios": "node config-generator.js && react-native run-ios",
    "build-ios": "node config-generator.js && react-native run-ios --configuration Release",
    "start-android": "node config-generator.js && react-native run-android",
    "build-android": "node config-generator.js && cd android/ && ./gradlew assembleRelease",
    ...
}

Ensuite, dans mes composants de services, importez simplement le fichier généré automatiquement:

import config from '../constants/config'

fetch(`${config.API_URL}/login`, params)
Toni Chaz
la source
3

Étape 1: Créez un composant séparé comme celui-ci Nom du composant: pagebase.js
Étape 2: À l'intérieur de ce code d'utilisation

    export const BASE_URL = "http://192.168.10.10:4848/";
    export const API_KEY = 'key_token';

Étape 3: Utilisez-le dans n'importe quel composant, pour l'utiliser, importez d'abord ce composant puis utilisez-le. Importez-le et utilisez-le:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY
Jitendra Suthar
la source
2

J'utilise babel-plugin-transform-inline-environment-variables.

Ce que j'ai fait, c'est mettre un fichier de configuration dans S3 avec mes différents environnements.

s3://example-bucket/dev-env.sh
s3://example-bucket/prod-env.sh
s3://example-bucket/stage-env.sh

CHAQUE fichier env:

FIRSTENV=FIRSTVALUE
SECONDENV=SECONDVALUE

Ensuite, j'ai ajouté un nouveau script dans mon package.jsonqui exécute un script pour le regroupement

if [ "$ENV" == "production" ]
then
  eval $(aws s3 cp s3://example-bucket/prod-env.sh - | sed 's/^/export /')
elif [ "$ENV" == "staging" ]
then
  eval $(aws s3 cp s3://example-bucket/stage-env.sh - | sed 's/^/export /')
else
  eval $(aws s3 cp s3://example-bucket/development-env.sh - | sed 's/^/export /')
fi

react-native start

Dans votre application, vous aurez probablement un fichier de configuration contenant:

const FIRSTENV = process.env['FIRSTENV']
const SECONDENV = process.env['SECONDENV']

qui sera remplacé par babel pour:

const FIRSTENV = 'FIRSTVALUE'
const SECONDENV = 'SECONDVALUE'

RAPPELEZ-VOUS que vous devez utiliser process.env['STRING']NOT process.env.STRINGou il ne convertira pas correctement.

Jack Zhang
la source
REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.Merci! C'est celui qui me fait trébucher !!!
Steven Yap
1

[Source] D'après ce que j'ai trouvé, il semble que par défaut, il est uniquement possible de faire des configurations de production et de développement (pas de staging ou d'autres environnements) - est-ce exact?

À l'heure actuelle, j'utilise un fichier environment.js qui peut être utilisé pour détecter les canaux de publication de l'expo et modifier les variables renvoyées en fonction de cela, mais pour la construction, je dois mettre à jour la variable non- DEV retournée pour être soit de mise en scène, soit prod:

import { Constants } from 'expo';
import { Platform } from 'react-native';
const localhost = Platform.OS === 'ios' ? 'http://localhost:4000/' : 'http://10.0.2.2:4000/';
const ENV = {
  dev: {
    apiUrl: localhost,
  },
  staging: {
    apiUrl: 'https://your-staging-api-url-here.com/'
  },
  prod: {
    apiUrl: 'https://your-prod-api-url-here.com/'
  },
}
const getEnvVars = (env = Constants.manifest.releaseChannel) => {
  // What is __DEV__ ?
  // This variable is set to true when react-native is running in Dev mode.
  // __DEV__ is true when run locally, but false when published.
  if (__DEV__) {
    return ENV.dev;
  } else {
    // When publishing to production, change this to `ENV.prod` before running an `expo build`
    return ENV.staging;
  }
}
export default getEnvVars;

Alternatives

Quelqu'un a-t-il de l'expérience avec react-native-dotenv pour des projets construits avec expo? J'aimerais entendre vos pensées

https://github.com/zetachang/react-native-dotenv

panchicore
la source
Vous pouvez définir autant de noms de canaux de version que vous le souhaitez et tester le nom afin de définir votre variable d'environnement. Là où je vois la limitation se trouve dans l'environnement de développement où releaseChannel n'est pas défini. Alors peut-être pourriez-vous utiliser babel-plugin-transform-inline-environment-variables - vous pourriez transmettre des variables d'environnement dans vos scripts et référencer process.env ['VAR_NAME'] dans votre fichier environment.js si dev?
colemerrick
0

vous pouvez également avoir différents scripts d'environnement: production.env.sh development.env.sh production.env.sh

Et puis source-les au début du travail [qui est juste lié à un alias] afin que tout le fichier sh ait est exporté pour chaque variable env:

export SOME_VAR=1234
export SOME_OTHER=abc

Et puis l'ajout de babel-plugin-transform-inline-environment-variables permettra d'y accéder dans le code:

export const SOME_VAR: ?string = process.env.SOME_VAR;
export const SOME_OTHER: ?string = process.env.SOME_OTHER;
Pikachu-go
la source
Ajoutez-vous quelque chose que @chapinkapa n'a pas dit?
Maximo Dominguez
0

La réponse de @ chapinkapa est bonne. Une approche que j'ai adoptée depuis que Mobile Center ne prend pas en charge les variables d'environnement, consiste à exposer la configuration de construction via un module natif:

Sur Android:

   @Override
    public Map<String, Object> getConstants() {
        final Map<String, Object> constants = new HashMap<>();
        String buildConfig = BuildConfig.BUILD_TYPE.toLowerCase();
        constants.put("ENVIRONMENT", buildConfig);
        return constants;
    } 

ou sur ios:

  override func constantsToExport() -> [String: Any]! {
    // debug/ staging / release
    // on android, I can tell the build config used, but here I use bundle name
    let STAGING = "staging"
    let DEBUG = "debug"

    var environment = "release"
    if let bundleIdentifier: String = Bundle.main.bundleIdentifier {
      if (bundleIdentifier.lowercased().hasSuffix(STAGING)) {
        environment = STAGING
      } else if (bundleIdentifier.lowercased().hasSuffix(DEBUG)){
        environment = DEBUG
      }
    }

    return ["ENVIRONMENT": environment]
  }

Vous pouvez lire la configuration de construction de manière synchrone et décider en Javascript comment vous allez vous comporter.

vonovak
la source
0

Il est possible d'accéder aux variables avec process.env.blablaau lieu de process.env['blabla']. Je l'ai récemment fait fonctionner et commenté comment je l'ai fait sur un problème sur GitHub car j'avais des problèmes avec le cache en fonction de la réponse acceptée. Voici le problème.

Srdjan Cosic
la source