Umgebungsvariable in React-Native setzen?

152

Ich verwende react-native, um eine plattformübergreifende App zu erstellen, aber ich weiß nicht, wie ich die Umgebungsvariable so einstellen soll, dass ich unterschiedliche Konstanten für unterschiedliche Umgebungen haben kann.

Beispiel:

development: 
  BASE_URL: '',
  API_KEY: '',
staging: 
  BASE_URL: '',
  API_KEY: '',
production:
  BASE_URL: '',
  API_KEY: '',
Damon Yuan
quelle
Sie können dies versuchenimport {Platform} from 'react-native'; console.log(Platform);
Praveen Prasad

Antworten:

138

Anstatt Ihre App-Konstanten fest zu codieren und die Umgebung zu wechseln (ich werde gleich erklären, wie das geht), empfehle ich, den Zwölf-Faktoren- Vorschlag zu verwenden, mit dem Ihr Erstellungsprozess Ihre BASE_URLund Ihre definiert API_KEY.

Um zu beantworten, wie Sie Ihre Umgebung aussetzen können react-native, schlage ich vor, Babels Babel-Plugin-Transformation-Inline-Umgebungsvariablen zu verwenden .

Damit dies funktioniert, müssen Sie das Plugin herunterladen und dann ein einrichten. .babelrcEs sollte ungefähr so ​​aussehen:

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

Wenn Sie also Ihren reaktionsnativen Code durch Ausführen API_KEY=my-app-id react-native bundle(oder Starten, Ausführen von ios oder Ausführen von Android) transpilieren, müssen Sie Ihren Code nur so aussehen lassen:

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

Und dann wird Babel das ersetzen durch:

const apiKey = 'my-app-id';

Hoffe das hilft!

Chapinkapa
quelle
7
Klingt nach einer großartigen Lösung, funktioniert aber bei [email protected] nicht. Die einzige Eigenschaft auf process.envist NODE_ENV.
Adam Faryna
2
Siehe die Antwort unten von Jack Zheng ... Sie können nicht über process.env.API_KEY... process.env['API_KEY']stattdessen auf die Variable zugreifen
Steven Yap
6
Ich erhalte die process.env ['API_KEY'] als undefiniert. Kann mir jemand helfen, dies einzurichten
user1106888
2
Ich hatte das gleiche Problem: undefiniert
Guto Marrara Marzagao
7
Funktioniert für mich in v0.56. Sie müssen den Bundler-Cache leeren, indem Sie ihn react-native start --reset-cachejedes Mal ausführen, wenn Sie die Umgebungsvariablen ändern.
Soheilpro
55

Die einfachste (nicht die beste oder ideale ) Lösung, die ich gefunden habe, war die Verwendung von react-native-dotenv . Sie fügen einfach die Voreinstellung "react-native-dotenv" zu Ihrer .babelrcDatei im Projektstamm hinzu:

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

Erstellen Sie eine .envDatei und fügen Sie Eigenschaften hinzu:

echo "SOMETHING=anything" > .env

Dann in Ihrem Projekt (JS):

import { SOMETHING } from 'react-native-dotenv'
console.log(SOMETHING) // "anything"
Slavo Vojacek
quelle
1
Ich hatte auf eine .env-basierte Lösung gehofft. Danke dir!
Anshul Koka
3
@Slavo Vojacek Wie verwende ich dies, um zum Beispiel eine base_urlfür beide stagingund zu konfigurieren production?
Compaq LE2202x
@ CompaqLE2202x Ich bin nicht ganz sicher, ob ich das verstehe? Fragen Sie nach der Verwendung unterschiedlicher .envDateien (pro Umgebung) oder nach der Wiederverwendung einiger Ihrer Werte in verschiedenen .envDateien, damit Sie sie nicht beispielsweise in Staging und Produktion duplizieren?
Slavo Vojacek
5
@SlavoVojacek Ich frage nach verschiedenen .envDateien pro Umgebung, sagen wir mal stagingund production.
Compaq LE2202x
@SlavoVojacek konnten Sie Werte in einer CI-Phase oder bei der Bereitstellung nicht überschreiben?
mgamsjager
37

Meiner Meinung nach ist die beste Option die Verwendung von react-native-config . Es unterstützt 12 Faktor .

Ich fand dieses Paket äußerst nützlich. Sie können mehrere Umgebungen festlegen, z. B. Entwicklung, Bereitstellung, Produktion.

Bei Android sind Variablen auch in Java-Klassen, gradle, AndroidManifest.xml usw. verfügbar. Bei iOS sind Variablen auch in Obj-C-Klassen, Info.plist, verfügbar.

Sie erstellen einfach Dateien wie

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

Sie füllen diese Dateien mit Schlüsselwerten wie

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

und dann benutze es einfach:

import Config from 'react-native-config'

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

Wenn Sie verschiedene Umgebungen verwenden möchten, setzen Sie die Variable ENVFILE grundsätzlich wie folgt:

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

oder zum Zusammenstellen der App für die Produktion (Android in meinem Fall):

cd android && ENVFILE=.env.production ./gradlew assembleRelease
Patrik Prevuznak
quelle
9
Es kann erwähnenswert sein, dass in der README- Datei Folgendes angegeben
zurückentwickeln.
Das Ding ist , es wird nicht die Arbeit mit einigen Frameworks wie twitter , die sie Keyset als com.twitter.sdk.android.CONSUMER_KEY in Ihrem .env haben erfordert
thibaut noah
Wenn Sie den Schlüssel in das Manifest einfügen möchten, wird er von der Erweiterung unterstützt. Es wird in dieser Antwort einfach nicht beschrieben. Sie können die Variablen in XML-, Java- und JS-Dateien verwenden.
Sfratini
4
React-Native-Config funktioniert nicht mit RN 0.56, es hat ungelöste Probleme und es wird über 6 Monate lang nicht gewartet. Das Problem, das seine Verwendung in RN beendet, ist github.com/luggit/react-native-config/issues/267 . Hier ist ein Hacking, damit es funktioniert. Github.com/luggit/react-native-config/issues/285
Marecky
24

React native hat nicht das Konzept globaler Variablen. Der modulare Geltungsbereich wird strikt durchgesetzt , um die Modularität und Wiederverwendbarkeit der Komponenten zu fördern.

Manchmal benötigen Sie jedoch Komponenten, um sich ihrer Umgebung bewusst zu sein. In diesem Fall ist es sehr einfach, ein EnvironmentModul zu definieren, das Komponenten aufrufen können, um Umgebungsvariablen abzurufen, zum Beispiel:

Umwelt.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

my-component.js

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

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

Dadurch wird eine Singleton- Umgebung erstellt, auf die von überall im Bereich Ihrer App zugegriffen werden kann. Sie müssen require(...)das Modul explizit aus allen Komponenten entfernen, die Umgebungsvariablen verwenden, aber das ist eine gute Sache.

tohster
quelle
19
Mein Problem ist, wie es geht getPlatform(). Ich habe eine Datei wie diese erstellt, kann aber die Logik hier in React Native
Damon Yuan
@DamonYuan, das hängt ganz davon ab, wie Sie Ihre Pakete einrichten. Ich habe keine Ahnung, was stagingoder was ich productionmeine, weil es von Ihrer Umgebung abhängt. Wenn Sie beispielsweise verschiedene Varianten für IOS und Android möchten, können Sie Environment initialisieren, indem Sie Ihre index.ios.jsund index.android.jsDateien importieren und die Plattform dort einstellen, z Environment.initialize('android').
Tohster
@DamonYuan macht das, was ich mache, überhaupt Hilfe, oder benötigen Sie weitere Erläuterungen?
Chapinkapa
Dies ist sehr schön, wenn Sie die Kontrolle über den Code haben. Ich verwende ein drittes
Teilmodul,
2
Wenn Sie eine env.jsDatei erstellen, ignorieren Sie diese beim Einchecken in das Repository und kopieren Sie die verwendeten Schlüssel mit leeren Zeichenfolgenwerten in eine andere env.js.exampleDatei, die Sie einchecken, damit andere Ihre App einfacher erstellen können. Wenn Sie versehentlich Projektgeheimnisse einchecken, sollten Sie den Verlauf neu schreiben , um ihn nicht nur aus der Quelle, sondern auch aus dem Verlauf zu entfernen.
Josh Habdas
17

Ich habe die __DEV__Polyfill verwendet, die in React-Native integriert ist, um dieses Problem zu lösen. Es wird automatisch auf eingestellt, truesolange Sie keine native Reaktion für die Produktion erstellen.

Z.B:

//vars.js

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

export {url, publicKey}

Dann nur import {url} from '../vars'und Sie werden immer die richtige bekommen. Leider funktioniert dies nicht, wenn Sie mehr als zwei Umgebungen möchten, aber es ist einfach und beinhaltet nicht das Hinzufügen weiterer Abhängigkeiten zu Ihrem Projekt.

Logister
quelle
Kennen Sie eine Möglichkeit, DEV auf TRUE zu setzen, selbst wenn Sie ein in xcode erstelltes Release erstellen?
Realtebo
1
Nee. Ich kommentiere nur die Produktvariablen aus und kopiere sie dann und füge sie in den Produktabschnitt ein, wenn ich einen Release-Build mit Entwicklungsvariablen erstellen möchte.
Logister
1
Ich fand dies die eleganteste Lösung
Dani Sh90
5

Die spezifische Methode zum Festlegen von Umgebungsvariablen hängt vom CI-Service, dem Build-Ansatz, der Plattform und den von Ihnen verwendeten Tools ab.

Wenn Sie Buddybuild for CI zum Erstellen einer App und zum Verwalten von Umgebungsvariablen verwenden und Zugriff auf die Konfiguration von JS benötigen, erstellen Sie einen env.js.examplemit Schlüsseln (mit leeren Zeichenfolgenwerten) zum Einchecken in die Quellcodeverwaltung und verwenden Sie Buddybuild zum Erstellen einer env.jsDatei zur Erstellungszeit im post-cloneSchritt, wobei der Dateiinhalt wie folgt aus den Erstellungsprotokollen ausgeblendet wird:

#!/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

Tipp: Vergessen Sie nicht, diese hinzuzufügen env.js, .gitignoredamit Konfiguration und Geheimnisse während der Entwicklung nicht versehentlich in die Quellcodeverwaltung eingecheckt werden.

Anschließend können Sie verwalten , wie die Datei , die geschrieben wird mit Buddybuild Variablen wie BUDDYBUILD_VARIANTSzum Beispiel eine größere Kontrolle über zu gewinnen , wie Sie Ihre Konfiguration bei der Erstellung erzeugt wird.

Josh Habdas
quelle
Insgesamt gefällt mir die Idee, aber wie funktioniert das env.js.exampleTeil? Angenommen, ich möchte die App in meiner lokalen Umgebung starten. wenn meine env.jsDatei in gitignore ist und env.js.examplewird als Umriss verwendet, das env.js.exampleist kein legitimer JS - Erweiterung, so dass ich bin nur ein wenig verwirrt darüber , was Sie von diesem Teil gemeint
volk
@volk Die env.js.exampleDatei befindet sich in der Codebasis als Referenzdokument, eine kanonische Quelle der Wahrheit darüber, welche Konfigurationsschlüssel die App verwenden möchte. Es beschreibt sowohl die zum Ausführen der App erforderlichen Schlüssel als auch den Dateinamen, der nach dem Kopieren und Umbenennen erwartet wird. Das Muster ist in Ruby-Apps üblich, die das Juwel dotenv verwenden , von dem ich das Muster entfernt habe.
Josh Habdas
3

Ich denke, so etwas wie die folgende Bibliothek könnte Ihnen helfen, das fehlende Teil des Puzzles, die Funktion getPlatform (), zu lösen.

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);
  });

Das einzige Problem, das ich dabei sehe, ist, dass es sich um asynchronen Code handelt. Es gibt eine Pull-Anfrage zur Unterstützung von getSync. Probieren Sie es auch aus.

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

leonfs
quelle
3
Upvoted für die Bereitstellung eines alternativen Ansatzes nicht erwähnt. Keine Einheitsgröße.
Josh Habdas
Die asynchrone Pull-Anforderung wurde in
jcollum
5
react-native-env scheint Android nicht zu unterstützen. Was ist der Punkt?
JCollum
3

Ich habe ein Pre-Build-Skript für das gleiche Problem erstellt, da ich einige verschiedene API-Endpunkte für die verschiedenen Umgebungen benötige

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')
})

Und ich habe eine benutzerdefinierte erstellt npm run scripts, um reaktionsnativen Lauf auszuführen .

Mein Paket-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",
    ...
}

Importieren Sie dann in meinen Servicekomponenten einfach die automatisch generierte Datei:

import config from '../constants/config'

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

Schritt 1: Erstellen Sie eine separate Komponente wie diese. Komponentenname: pagebase.js
Schritt 2: Verwenden Sie in diesem Code diesen Code

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

Schritt 3: Verwenden Sie es in einer beliebigen Komponente. Um es zu verwenden, importieren Sie zuerst diese Komponente und verwenden Sie sie dann. Importieren Sie es und verwenden Sie es:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY
Jitendra Suthar
quelle
2

Ich benutze babel-plugin-transform-inline-environment-variables.

Ich habe Konfigurationsdateien in S3 mit meinen verschiedenen Umgebungen abgelegt.

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

JEDE env-Datei:

FIRSTENV=FIRSTVALUE
SECONDENV=SECONDVALUE

Danach habe ich ein neues Skript hinzugefügt package.json, das ein Skript zum Bündeln ausführt

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

In Ihrer App haben Sie wahrscheinlich eine Konfigurationsdatei mit:

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

welches durch babel ersetzt wird zu:

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

Denken Sie daran, Sie müssen process.env['STRING']NOT verwenden, sonst process.env.STRINGwird es nicht richtig konvertiert.

Jack Zhang
quelle
REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.Vielen Dank! Dies ist derjenige, der mich stolpert !!!
Steven Yap
1

[Quelle] Nach dem, was ich gefunden habe, sieht es standardmäßig so aus, dass nur Produktions- und Entwicklungskonfigurationen möglich sind (kein Staging oder andere Umgebungen) - stimmt das?

Im Moment habe ich eine Datei environment.js verwendet, mit der Expo-Release-Kanäle erkannt und die darauf zurückgegebenen Variablen geändert werden können. Zum Erstellen muss ich jedoch die Nicht- DEV- Variable aktualisieren, die entweder als Staging oder als Staging zurückgegeben wird 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;

Alternativen

Hat jemand Erfahrung mit React-Native-Dotenv für Projekte, die mit expo erstellt wurden? Ich würde gerne deine Gedanken hören

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

Panchicore
quelle
Sie können beliebig viele Release-Kanalnamen definieren und den Namen testen, um Ihre Umgebungsvariable zu definieren. Wo ich sehe, liegt die Einschränkung in der Entwicklungsumgebung, in der releaseChannel undefiniert ist. Vielleicht könnten Sie also babel-plugin-transform-inline-Umgebungsvariablen verwenden - Sie könnten Umgebungsvariablen in Ihren Skripten weitergeben und auf process.env ['VAR_NAME'] in Ihrer Datei environment.js verweisen, wenn dev?
Colemerrick
0

Sie können auch verschiedene Env-Skripte verwenden: Production.env.sh Development.env.sh Production.env.sh

Und geben Sie sie dann ein, wenn Sie mit der Arbeit beginnen [was nur an einen Alias ​​gebunden ist], sodass die sh-Datei nur für jede env-Variable exportiert wird:

export SOME_VAR=1234
export SOME_OTHER=abc

Wenn Sie dann babel-plugin-transform-inline-Umgebungsvariablen hinzufügen, können Sie im Code darauf zugreifen:

export const SOME_VAR: ?string = process.env.SOME_VAR;
export const SOME_OTHER: ?string = process.env.SOME_OTHER;
Pikachu-go
quelle
Fügen Sie etwas hinzu, was @chapinkapa nicht gesagt hat?
Maximo Dominguez
0

@ Chapinkapas Antwort ist gut. Ein Ansatz, den ich gewählt habe, da Mobile Center keine Umgebungsvariablen unterstützt, besteht darin, die Build-Konfiguration über ein natives Modul verfügbar zu machen:

Auf 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;
    } 

oder auf 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]
  }

Sie können die Build-Konfiguration synchron lesen und in Javascript entscheiden, wie Sie sich verhalten möchten.

vonovak
quelle
0

Es ist möglich, auf die Variablen mit process.env.blablastatt auf zuzugreifen process.env['blabla']. Ich habe es kürzlich zum Laufen gebracht und kommentiert, wie ich es bei einem Problem auf GitHub gemacht habe, weil ich aufgrund der akzeptierten Antwort einige Probleme mit dem Cache hatte. Hier ist das Problem.

Srdjan Cosic
quelle