Definir variável de ambiente em react-native?

152

Estou usando o react-native para criar um aplicativo de plataforma cruzada, mas não sei como definir a variável de ambiente para que eu possa ter constantes diferentes para ambientes diferentes.

Exemplo:

development: 
  BASE_URL: '',
  API_KEY: '',
staging: 
  BASE_URL: '',
  API_KEY: '',
production:
  BASE_URL: '',
  API_KEY: '',
Damon Yuan
fonte
você pode tentar issoimport {Platform} from 'react-native'; console.log(Platform);
Praveen Prasad 24/10

Respostas:

138

Em vez de codificar constantemente as constantes de seu aplicativo e mudar o ambiente (explicarei como fazer isso em um momento), sugiro usar a sugestão de doze fatores para que seu processo de criação defina o seu BASE_URLe o seu API_KEY.

Para responder como expor seu ambiente react-native, sugiro usar as variáveis ​​de ambiente babel-plugin-transform-inline-environment .

Para que isso funcione, é necessário fazer o download do plug-in e, em seguida, você precisará configurar um .babelrce deve ser algo como isto:

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

E assim, se você transpilar seu código nativo de reação executando API_KEY=my-app-id react-native bundle(ou iniciar, executar ios ou executar android), tudo o que você precisa fazer é fazer com que seu código fique assim:

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

E então Babel substituirá isso por:

const apiKey = 'my-app-id';

Espero que isto ajude!

chapinkapa
fonte
7
Parece uma ótima solução, mas não funciona para mim no [email protected]. A única propriedade em process.envé NODE_ENV.
Adam Faryna
2
Veja a resposta abaixo por Jack Zheng ... você não pode acessar a variável via process.env.API_KEY... uso process.env['API_KEY']em vez
Steven Yap
6
Estou recebendo o process.env ['API_KEY'] como indefinido. Alguém pode me ajudar a configurar isso
user1106888
2
Eu tive o mesmo problema: indefinido
Guto Marrara Marzagão
7
Funciona para mim na v0.56. Você precisa limpar o cache do empacotador executando react-native start --reset-cachesempre que alterar as variáveis ​​de ambiente.
soheilpro
55

A solução mais simples (não a melhor ou a ideal ) que encontrei foi usar react-native-dotenv . Você simplesmente adiciona a predefinição "react-native-dotenv" ao seu .babelrcarquivo na raiz do projeto, da seguinte maneira:

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

Crie um .envarquivo e adicione propriedades:

echo "SOMETHING=anything" > .env

Então no seu projeto (JS):

import { SOMETHING } from 'react-native-dotenv'
console.log(SOMETHING) // "anything"
Slavo Vojacek
fonte
1
Eu esperava uma solução baseada em .env. Obrigado!
Anshul Koka
3
@Slavo Vojacek Como usar isso para configurar, por exemplo, um base_urlpara ambos staginge production?
Compaq LE2202x 21/02
@ CompaqLE2202x Não sei bem se entendi? Você está perguntando sobre o uso de .envarquivos diferentes (por ambiente) ou sobre a reutilização de alguns de seus valores em .envarquivos diferentes , para não duplicá-los entre, por exemplo, Estadiamento e produção?
Slavo Vojacek 17/03/19
5
@SlavoVojacek Estou perguntando sobre .envarquivos diferentes por ambiente, digamos staginge production.
Compaq LE2202x
@SlavoVojacek não foi possível sobrescrever valores em um estágio de IC ou na implantação?
mgamsjager
37

Na minha opinião, a melhor opção é usar o react-native-config . Suporta 12 fatores .

Achei este pacote extremamente útil. Você pode definir vários ambientes, por exemplo, desenvolvimento, preparação, produção.

No caso do Android, as variáveis ​​também estão disponíveis nas classes Java, gradle, AndroidManifest.xml etc. No caso do iOS, as variáveis ​​também estão disponíveis nas classes Obj-C, Info.plist.

Você acabou de criar arquivos como

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

Você preenche esses arquivos com chave, valores como

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

e depois use:

import Config from 'react-native-config'

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

Se você deseja usar ambientes diferentes, basicamente define a variável ENVFILE como esta:

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

ou para montar o aplicativo para produção (android no meu caso):

cd android && ENVFILE=.env.production ./gradlew assembleRelease
Patrik Prevuznak
fonte
9
Pode ser interessante notar que, no README, declara Lembre-se de que este módulo não ofusca nem criptografa segredos para o empacotamento, portanto, não armazene chaves sensíveis em .env. É basicamente impossível impedir os utilizadores de engenharia reversa segredos app móvel, de modo projetar seu aplicativo (e APIs) com isso em mente
Marklar
Coisa é ele não funcionará com alguns frameworks como o twitter, que exige ter que conjunto de chaves como com.twitter.sdk.android.CONSUMER_KEY em sua .env
thibaut noah
Se você quer dizer colocar a chave dentro do manifesto, a extensão o suporta. Apenas não está descrito nesta resposta. Você pode usar as variáveis ​​nos arquivos XML, Java e JS.
Sfratini
4
O react-native-config não funciona com o RN 0.56, possui problemas não resolvidos e permanece sem manutenção por mais de 6 meses. O problema que mata seu uso no RN é github.com/luggit/react-native-config/issues/267 , aqui estão alguns hackers para fazê-lo funcionar github.com/luggit/react-native-config/issues/285
Marecky
24

O reagir nativo não tem o conceito de variáveis ​​globais. Ele aplica o escopo modular estritamente, a fim de promover a modularidade e a reutilização de componentes.

Às vezes, porém, você precisa de componentes para conhecer o ambiente deles. Nesse caso, é muito simples definir um Environmentmódulo que componentes podem chamar para obter variáveis ​​de ambiente, por exemplo:

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

Isso cria um ambiente singleton que pode ser acessado de qualquer lugar dentro do escopo do seu aplicativo. Você precisa explicitamente require(...)o módulo de qualquer componente que use variáveis ​​de ambiente, mas isso é uma coisa boa.

tohster
fonte
19
meu problema é como getPlatform(). Eu tenho fazer um arquivo como este, mas não pode terminar a lógica aqui em Reagir Native
Damon Yuan
@ DamonYuan, que depende inteiramente de como você está configurando seus pacotes. Não faço ideia do que stagingou productionaté mesmo quero dizer, porque depende do seu ambiente. Por exemplo, se você quiser sabores diferentes para IOS vs Android, então você pode inicializar Ambiente importando-os index.ios.jse index.android.jsficheiros e definir a plataforma lá, por exemplo Environment.initialize('android').
tohster
@DamonYuan faz o que eu ajudo, ou você precisa de mais algum esclarecimento?
chapinkapa
Isso é muito bom quando você tem controle sobre o código. Estou executando um terceiro módulo parte que se baseia em process.env, então ...
enapupe
2
Se você criar um env.jsarquivo, certifique-se de ignorá-lo dos check-ins no repositório e copie as chaves usadas, com valores de sequência vazios, em outro env.js.examplearquivo em que você faz o check-in para que outras pessoas possam criar seu aplicativo mais facilmente. Se você acidentalmente verificar os segredos do projeto, considere reescrever o histórico para removê-los não apenas da fonte, mas também do histórico.
Josh Habdas 22/09/16
17

Eu usei o __DEV__polyfill que é incorporado ao react-native para resolver esse problema. Ele é definido automaticamente truecomo contanto que você não construa reagir nativo para produção.

Por exemplo:

//vars.js

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

export {url, publicKey}

Então apenas import {url} from '../vars'e você sempre obterá o correto. Infelizmente, isso não funcionará se você quiser mais de dois ambientes, mas é fácil e não envolve adicionar mais dependências ao seu projeto.

Logister
fonte
você conhece uma maneira de 'focar' o DEV em VERDADEIRO, mesmo ao criar uma versão compilada no xcode?
Realtebo 16/04
1
Não. Eu apenas comento os prod vars e, em seguida, copio e colo os dev vars na seção prod quando quero fazer um release com variáveis ​​dev.
Logister
1
Eu encontrei esta solução elegante mais
Dani SH90
5

O método específico usado para definir variáveis ​​de ambiente varia de acordo com o serviço de IC, a abordagem de construção, a plataforma e as ferramentas que você está usando.

Se você estiver usando o Buddybuild for CI para criar um aplicativo e gerenciar variáveis ​​de ambiente , e precisar acessar a configuração do JS, crie um env.js.examplecom chaves (com valores de cadeia vazios) para fazer check-in no controle de origem e use o Buddybuild para produzir um env.jsno momento da construção post-clone, ocultando o conteúdo do arquivo dos logs de construção, da seguinte maneira:

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

Dica: Não se esqueça de adicionar env.jspara .gitignoreque a configuração e os segredos não sejam verificados acidentalmente no controle de origem durante o desenvolvimento.

Você pode gerenciar como o arquivo é gravado usando as variáveis ​​Buddybuild, como BUDDYBUILD_VARIANTS, por exemplo, para obter maior controle sobre como sua configuração é produzida no momento da criação.

Josh Habdas
fonte
No geral, eu gosto da ideia, mas como a env.js.examplepeça funciona? Digamos que eu queira iniciar o aplicativo no meu ambiente local. se meu env.jsarquivo está em gitignore e env.js.exampleé usado como um esboço, o env.js.examplenão é uma extensão legítima JS, então eu só estou um pouco confuso sobre o que você quis dizer com essa parte
Volk
@volk O env.js.examplearquivo fica na base de código como um documento de referência, uma fonte canônica da verdade sobre quais chaves de configuração o aplicativo deseja consumir. Ambos descrevem as chaves necessárias para executar o aplicativo, bem como o nome do arquivo esperado, uma vez copiado e renomeado. O padrão é comum em aplicativos Ruby usando a gema dotenv , que é de onde eu tirei o padrão.
Josh Habdas 27/09/16
3

Penso que algo como a seguinte biblioteca poderia ajudá-lo a resolver o pedaço que faltava no quebra-cabeça, a função 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);
  });

O único problema que vejo com isso, é o código assíncrono. Há uma solicitação pull para oferecer suporte ao getSync. Confira também.

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

leonfs
fonte
3
Promovido por fornecer uma abordagem alternativa não mencionada. Nenhum tamanho serve para todos.
Josh Habdas 22/09/16
A req asynch puxar foi fundida em
jcollum
5
O react-native-env não parece suportar o Android. Qual é o objetivo?
Jcollum # 16/16
3

Eu criei um script de pré-construção para o mesmo problema, porque eu preciso de alguns pontos de extremidade da API diferentes para os diferentes ambientes

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

E eu criei um personalizado npm run scriptspara executar a execução nativa de reação ..

Meu pacote-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",
    ...
}

Em meus componentes de serviços, basta importar o arquivo gerado automaticamente:

import config from '../constants/config'

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

Etapa 1: Crie um componente separado como este Nome do componente: pagebase.js
Etapa 2: Dentro deste código de uso,

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

Etapa 3: use-o em qualquer componente; para usá-lo, importe-o primeiro e depois use-o. Importe-o e use-o:

        import * as base from "./pagebase";

        base.BASE_URL
        base.API_KEY
Jitendra Suthar
fonte
2

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

O que fiz foi colocar um arquivo de configuração no S3 com meus diferentes ambientes.

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

CADA arquivo env:

FIRSTENV=FIRSTVALUE
SECONDENV=SECONDVALUE

Depois, adicionei um novo script no meu package.jsonque executa um 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 do seu aplicativo, você provavelmente terá um arquivo de configuração que possui:

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

que será substituído por babel para:

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

Lembre-se de que você deve usar process.env['STRING']NOT process.env.STRINGou ele não será convertido corretamente.

Jack Zhang
fonte
REMEMBER you have to use process.env['STRING'] NOT process.env.STRING or it won't convert properly.Obrigado! Esse é o que mais me engana !!!
Steven Yap
1

[Fonte] Pelo que descobri, parece que por padrão, só é possível fazer configurações de produção e desenvolvimento (sem preparação ou outros ambientes) - está certo?

No momento, estou usando um arquivo environment.js que pode ser usado para detectar canais de liberação expo e alterar as variáveis ​​retornadas com base nisso, mas, para criar, preciso atualizar a variável não DEV retornada para preparação ou 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;

Alternativas

alguém tem experiência usando react-native-dotenv para projetos criados com expo? Eu adoraria ouvir seus pensamentos

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

panchicore
fonte
Você pode definir quantos nomes de canais de liberação desejar e testar o nome para definir sua variável de ambiente. Onde vejo que a limitação está no ambiente de desenvolvimento em que releaseChannel é indefinido. Então, talvez você possa usar babel-plugin-transform-inline-environment-variables - você pode passar variáveis ​​de ambiente em seus scripts e processos de referência.env ['VAR_NAME'] em seu arquivo environment.js, se dev?
Colemerrick 26/08/19
0

você também pode ter scripts env diferentes: production.env.sh development.env.sh production.env.sh

E depois forneça-os quando começar a trabalhar [que está apenas vinculado a um alias], para que todo o arquivo sh tenha seja exportado para cada variável env:

export SOME_VAR=1234
export SOME_OTHER=abc

E adicionar babel-plugin-transform-inline-environment-variables permitirá acessá-las no código:

export const SOME_VAR: ?string = process.env.SOME_VAR;
export const SOME_OTHER: ?string = process.env.SOME_OTHER;
Pikachu-go
fonte
Você está adicionando algo que @chapinkapa não disse?
Maximo Dominguez
0

A resposta de @ chapinkapa é boa. Uma abordagem adotada desde que o Mobile Center não oferece suporte a variáveis ​​de ambiente é expor a configuração de compilação por meio de um módulo nativo:

No 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 no 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]
  }

Você pode ler a configuração da compilação de forma síncrona e decidir em Javascript como vai se comportar.

vonovak
fonte
0

É possível acessar as variáveis ​​com em process.env.blablavez de process.env['blabla']. Recentemente, fiz o trabalho e comentei sobre como o fiz em um problema no GitHub porque tive alguns problemas com o cache com base na resposta aceita. Aqui está a questão.

Srdjan Cosic
fonte