Configuración de la variable de entorno en react-native?

152

Estoy usando react-native para construir una aplicación multiplataforma, pero no sé cómo configurar la variable de entorno para poder tener diferentes constantes para diferentes entornos.

Ejemplo:

development: 
  BASE_URL: '',
  API_KEY: '',
staging: 
  BASE_URL: '',
  API_KEY: '',
production:
  BASE_URL: '',
  API_KEY: '',
Damon Yuan
fuente
puedes probar estoimport {Platform} from 'react-native'; console.log(Platform);
Praveen Prasad

Respuestas:

138

En lugar de codificar constantemente las constantes de su aplicación y hacer un cambio en el entorno (explicaré cómo hacerlo en un momento), sugiero usar la sugerencia de doce factores para que su proceso de compilación defina su BASE_URLy suAPI_KEY .

Para responder a cómo exponer su entorno react-native, sugiero usar las variables babel-plugin-transform-inline-environment-variables de Babel .

Para que esto funcione, debe descargar el complemento y luego deberá configurar un .babelrcy debería verse así:

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

Entonces, si transpila su código nativo de reacción ejecutando API_KEY=my-app-id react-native bundle(o start, run-ios o run-android), todo lo que tiene que hacer es hacer que su código se vea así:

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

Y luego Babel lo reemplazará con:

const apiKey = 'my-app-id';

¡Espero que esto ayude!

chapinkapa
fuente
77
Suena como una gran solución, pero no funciona para mí en [email protected]. La única propiedad en process.enves NODE_ENV.
Adam Faryna
2
Vea la respuesta a continuación de Jack Zheng ... no puede acceder a la variable a través de process.env.API_KEY... use process.env['API_KEY']en su lugar
Steven Yap
66
Obtengo el process.env ['API_KEY'] como indefinido. ¿Alguien puede ayudarme a configurar esto
User1106888
2
Tuve el mismo problema: indefinido
Guto Marrara Marzagao
77
Funciona para mí en v0.56. Debe borrar el caché del bundler ejecutándose react-native start --reset-cachecada vez que cambie las variables de entorno.
soheilpro
55

La solución más simple (no la mejor o la ideal ) que encontré fue usar react-native-dotenv . Simplemente agregue el preset "react-native-dotenv" a su .babelrcarchivo en la raíz del proyecto de la siguiente manera:

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

Crea un .envarchivo y agrega propiedades:

echo "SOMETHING=anything" > .env

Luego en su proyecto (JS):

import { SOMETHING } from 'react-native-dotenv'
console.log(SOMETHING) // "anything"
Slavo Vojacek
fuente
1
Esperaba una solución basada en .env. ¡Gracias!
Anshul Koka
3
@Slavo Vojacek ¿Cómo uso esto para configurar, por ejemplo, uno base_urlpara ambos stagingy production?
Compaq LE2202x
@ CompaqLE2202x No estoy seguro de entenderlo. ¿Está preguntando sobre el uso de diferentes .envarchivos (por entorno) o sobre la reutilización de algunos de sus valores en diferentes .envarchivos, para no duplicarlos, por ejemplo, por etapas y producción?
Slavo Vojacek
55
@SlavoVojacek Estoy preguntando sobre diferentes .envarchivos por entorno, digamos stagingy production.
Compaq LE2202x
@SlavoVojacek, ¿no podría sobrescribir los valores en una etapa de CI o en la implementación?
mgamsjager
37

En mi opinión, la mejor opción es usar react-native-config . Es compatible con 12 factores .

Encontré este paquete extremadamente útil. Puede configurar múltiples entornos, por ejemplo, desarrollo, puesta en escena, producción.

En el caso de Android, las variables están disponibles también en las clases Java, gradle, AndroidManifest.xml, etc. En el caso de iOS, las variables también están disponibles en las clases Obj-C, Info.plist.

Simplemente creas archivos como

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

Rellena estos archivos con clave, valores como

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

y luego solo utilízalo:

import Config from 'react-native-config'

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

Si desea utilizar diferentes entornos, básicamente configura la variable ENVFILE de esta manera:

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

o para ensamblar aplicaciones para producción (Android en mi caso):

cd android && ENVFILE=.env.production ./gradlew assembleRelease
Patrik Prevuznak
fuente
9
Vale la pena señalar que en el archivo README dice Tenga en cuenta que este módulo no ofusca ni encripta secretos para el empaquetado, por lo tanto, no almacene claves confidenciales en .env. Es básicamente imposible evitar que los usuarios realicen ingeniería inversa de los secretos de las aplicaciones móviles, así que diseñe su aplicación (y API) teniendo esto en cuenta
Marklar
La cuestión es que no funcionará con algunos frameworks como Twitter, lo que requiere que se configuren como com.twitter.sdk.android.CONSUMER_KEY en su .env
thibaut noah
Si quiere decir poner la llave dentro del Manifiesto, la extensión lo admite. Simplemente no se describe en esta respuesta. Puede usar las variables en archivos XML, Java y JS.
sfratini
44
react-native-config no funciona con RN 0.56, tiene problemas no resueltos y no se mantiene durante más de 6 meses. El problema que la bruja mata su uso en RN es github.com/luggit/react-native-config/issues/267 , aquí hay algunos trucos
Marecky
24

React native no tiene el concepto de variables globales. Hace cumplir el alcance modular estrictamente el para promover la modularidad y la reutilización de los componentes.

A veces, sin embargo, necesita componentes para conocer su entorno. En este caso, es muy simple definir un Environmentmódulo al que los componentes pueden llamar para obtener variables de entorno, por ejemplo:

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

my-component.js

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

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

Esto crea un entorno singleton al que se puede acceder desde cualquier lugar dentro del alcance de su aplicación. Debe explícitamente require(...)el módulo desde cualquier componente que use variables de entorno, pero eso es algo bueno.

tohster
fuente
19
Mi problema es cómo hacerlo getPlatform(). He creado un archivo como este, pero no puedo terminar la lógica aquí en React Native
Damon Yuan
@DamonYuan que depende completamente de cómo esté configurando sus paquetes. No tengo ni idea de qué, stagingni productionsiquiera quiero decir, porque depende de su entorno. Por ejemplo, si quieres diferentes sabores para iOS vs Android a continuación, puede inicializar el Medio Ambiente mediante la importación de sus index.ios.jsy index.android.jsarchivos y el establecimiento de la plataforma de allí, por ejemplo Environment.initialize('android').
tohster
@DamonYuan hace lo que puse ayuda, ¿o necesitas más aclaraciones?
chapinkapa
Esto es muy bueno cuando tienes control sobre el código. Estoy ejecutando un módulo de tercera parte que se basa en process.env, así que ...
enapupe
2
Si crea un env.jsarchivo, asegúrese de ignorarlo desde los registros en el repositorio y copie las claves utilizadas, con valores de cadena vacíos, en otro env.js.examplearchivo que registre para que otros puedan construir su aplicación más fácilmente. Si accidentalmente registra los secretos del proyecto, considere volver a escribir el historial para eliminarlos no solo de la fuente sino también del historial de los mismos.
Josh Habdas
17

Utilicé el __DEV__polyfill que está integrado en react-native para resolver este problema. Se configura automáticamente en truetanto que no esté creando reacción nativa para la producción.

P.ej:

//vars.js

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

export {url, publicKey}

Entonces solo import {url} from '../vars'y siempre obtendrás el correcto. Desafortunadamente, esto no funcionará si desea más de dos entornos, pero es fácil y no implica agregar más dependencias a su proyecto.

Logister
fuente
¿Conoces una forma de 'forzar' DEV a VERDADERO incluso cuando creas una versión de lanzamiento en xcode?
realtebo
1
No Solo comento las variables de producción y luego copio y pego las variables de desarrollo en la sección de producción cuando quiero hacer una compilación de lanzamiento con variables de desarrollo.
Logister
1
Encontré esta la solución más elegante
Dani Sh90
5

El método específico utilizado para establecer variables de entorno variará según el servicio de CI, el enfoque de compilación, la plataforma y las herramientas que esté utilizando.

Si está utilizando Buddybuild para CI para construir una aplicación y administrar variables de entorno , y necesita acceso a la configuración desde JS, cree una env.js.examplecon claves (con valores de cadena vacíos) para registrarse en el control de origen y use Buddybuild para producir un env.jsarchivo en tiempo de compilación en el post-clonepaso, ocultando el contenido del archivo de los registros de compilación, así:

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

Consejo: No olvide agregar env.jspara .gitignoreque la configuración y los secretos no se verifiquen accidentalmente en el control de origen durante el desarrollo.

A continuación, puede administrar la forma en que el archivo se escribe utilizando las variables de Buddybuild como BUDDYBUILD_VARIANTS, por ejemplo, para obtener un mayor control sobre la forma en que su configuración se produce en tiempo de compilación.

Josh Habdas
fuente
En general, me gusta la idea, pero ¿cómo funciona la env.js.examplepieza? Digamos que quiero iniciar la aplicación en mi entorno local. si mi env.jsarchivo está en gitignore y env.js.examplese usa como un esquema, env.js.exampleno es una extensión JS legítima, así que estoy un poco confundido sobre lo que quisiste decir con esta parte
volk
@volk El env.js.examplearchivo se encuentra en la base de código como documento de referencia, una fuente canónica de verdad sobre qué claves de configuración quiere consumir la aplicación. Ambos describen las claves necesarias para ejecutar la aplicación, así como el nombre de archivo esperado una vez copiado y renombrado. El patrón es común en las aplicaciones de Ruby que usan la gema dotenv , que es de donde saqué el patrón.
Josh Habdas
3

Creo que algo como la siguiente biblioteca podría ayudarlo a resolver la parte faltante del rompecabezas, la función 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);
  });

El único problema que veo con esto es que es un código asíncrono. Hay una solicitud de extracción para admitir getSync. Compruébalo también.

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

leonfs
fuente
3
Votado por proporcionar un enfoque alternativo no mencionado. No hay una talla única para todos.
Josh Habdas
El asynch pull req se ha fusionado
jcollum
55
react-native-env no parece ser compatible con Android. ¿Cuál es el punto de?
jcollum
3

He creado un script de precompilación para el mismo problema porque necesito algunos puntos finales de API diferentes para los diferentes entornos.

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

Y he creado una costumbre npm run scriptspara ejecutar react-native.

Mi paquete-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",
    ...
}

Luego, en mis componentes de servicios, simplemente importe el archivo generado automáticamente:

import config from '../constants/config'

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

Paso 1: Cree un componente separado como este Nombre del componente: pagebase.js
Paso 2: Dentro de este código de uso, esto

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

Paso 3: Úselo en cualquier componente, para usarlo primero importe este componente y luego úselo. Importarlo y usarlo:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY
Jitendra Suthar
fuente
2

Yo uso babel-plugin-transform-inline-environment-variables.

Lo que hice fue poner un archivo de configuración dentro de S3 con mis diferentes entornos.

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

CADA archivo env:

FIRSTENV=FIRSTVALUE
SECONDENV=SECONDVALUE

Luego, agregué un nuevo script en mi package.jsonque ejecuta un script para agrupar

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

Dentro de su aplicación, probablemente tendrá un archivo de configuración que tiene:

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

que será reemplazado por babel para:

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

RECUERDA que debes usar process.env['STRING']NOT process.env.STRINGo no se convertirá correctamente.

Jack Zhang
fuente
REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.¡Gracias! ¡Este es el que me hace tropezar!
Steven Yap
1

[Fuente] Por lo que he encontrado, parece que por defecto, solo es posible hacer configuraciones de producción y desarrollo (sin escenarios u otros entornos), ¿es eso correcto?

En este momento, he estado usando un archivo environment.js que se puede usar para detectar canales de publicación de expo y cambiar las variables devueltas en función de eso, pero para compilar, necesito actualizar la variable que no es DEV devuelta para ser puesta en escena o pinchar:

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;

Alternativas

¿Alguien tiene experiencia con react-native-dotenv para proyectos creados con expo? Me encantaría escuchar tus pensamientos

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

panchicore
fuente
Puede definir tantos nombres de canales de lanzamiento como desee y probar el nombre para definir su variable de entorno. Donde veo la limitación es en el entorno de desarrollo donde releaseChannel no está definido. Entonces, tal vez podría usar babel-plugin-transform-inline-environment-variables: podría transmitir variables de entorno en sus scripts y hacer referencia a process.env ['VAR_NAME'] en su archivo environment.js si dev?
colemerrick
0

también puede tener diferentes scripts env: production.env.sh development.env.sh production.env.sh

Y luego instálelos cuando comience a funcionar [que está vinculado a un alias] para que todo el archivo sh se exporte para cada variable env:

export SOME_VAR=1234
export SOME_OTHER=abc

Y luego agregar babel-plugin-transform-inline-environment-variables permitirá acceder a ellas en el código:

export const SOME_VAR: ?string = process.env.SOME_VAR;
export const SOME_OTHER: ?string = process.env.SOME_OTHER;
Pikachu-go
fuente
¿Estás agregando algo que @chapinkapa no ha dicho?
Maximo Dominguez
0

La respuesta de @ chapinkapa es buena. Un enfoque que he tomado desde que Mobile Center no admite variables de entorno es exponer la configuración de compilación a través de un módulo nativo:

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

o en 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]
  }

Puede leer la configuración de compilación sincrónicamente y decidir en Javascript cómo se va a comportar.

vonovak
fuente
0

Es posible acceder a las variables con en process.env.blablalugar de process.env['blabla']. Recientemente lo hice funcionar y comenté cómo lo hice en un problema en GitHub porque tuve algunos problemas con el caché en función de la respuesta aceptada. Aquí está el problema.

Srdjan Cosic
fuente