Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

.env variables are cached #422

Open
2 of 3 tasks
AbdulBsit opened this issue Mar 16, 2023 · 32 comments
Open
2 of 3 tasks

.env variables are cached #422

AbdulBsit opened this issue Mar 16, 2023 · 32 comments
Assignees

Comments

@AbdulBsit
Copy link

  • Asked question in discussions
  • Tried the troubleshooting Wiki
  • Followed the migration Wiki

Describe the bug
I tried upgrading, dev dependencies releated to babel, but no luck, it cached the variable, until we change something in to the file we imported the "@env", when we undo the change, it goes back to cached value

Expected behavior
Change in .env should reflect in files, after a restart

Additional context

"@babel/core": "^7.21.3",
"@babel/runtime": "^7.21.0",
"react-native-dotenv": "^3.4.7",
@github-actions
Copy link

Hey, thank you for opening this issue! 🙂 To boost priority on this issue and support open source please tip the team at https://issuehunt.io/r/goatandsheep/react-native-dotenv/issues/422

@goatandsheep
Copy link
Owner

Hi @AbdulBsit those Babel package versions are only really if you're using something overwrites the versions such as expo. Are you using expo? Also are the babel packages installed as dependencies or resolutions? Please tell me what else you tried that didn't work in the cacheing section of the README as I'm unable to reproduce this.

@AbdulBsit
Copy link
Author

AbdulBsit commented Mar 18, 2023

Hey @goatandsheep
These babel packages are installed as devDependencies, and I am not using expo

Also things i have tried which not solves the issue

  • Reset cache
yarn start --reset-cache
  • Adding resolutions to my package.json
"resolutions": {
  "@babel/core": "^7.20.2",
  "babel-loader": "^8.3.0"
}

For your reference here is my current configuration

package.json

{
  "name": "app",
  "version": "0.0.1",
  "private": true,
  "scripts": {
    "android": "react-native run-android",
    "postinstall": "patch-package",
    "ios": "react-native run-ios",
    "start": "react-native start",
    "test": "jest",
    "lint": "eslint . --ext .js,.jsx,.ts,.tsx"
  },
  "dependencies": {
    "@bandwidth/webrtc-browser": "^0.13.1",
    "@gorhom/bottom-sheet": "^4.4.5",
    "@notifee/react-native": "^7.3.0",
    "@react-native-async-storage/async-storage": "~1.15.0",
    "@react-native-clipboard/clipboard": "^1.11.1",
    "@react-native-community/datetimepicker": "^6.7.1",
    "@react-native-community/netinfo": "^9.3.7",
    "@react-native-firebase/app": "^16.5.0",
    "@react-native-firebase/messaging": "^16.5.0",
    "@react-native-menu/menu": "^0.7.3",
    "@react-navigation/bottom-tabs": "^6.0.9",
    "@react-navigation/drawer": "^6.5.0",
    "@react-navigation/material-top-tabs": "^6.0.6",
    "@react-navigation/native": "^6.0.6",
    "@react-navigation/stack": "^6.0.11",
    "@sentry/react-native": "^5.0.0-alpha.10",
    "appcenter": "4.4.5",
    "appcenter-analytics": "4.4.5",
    "appcenter-crashes": "4.4.5",
    "axios": "^0.21.1",
    "events": "^3.3.0",
    "jwt-decode": "^3.1.2",
    "moment": "^2.29.1",
    "newrelic-react-native-agent": "0.0.9",
    "react": "18.1.0",
    "react-native": "^0.70.7",
    "react-native-action-sheet": "^2.2.0",
    "react-native-autolink": "^3.0.0",
    "react-native-background-timer": "^2.4.1",
    "react-native-callkeep": "^4.3.6",
    "react-native-clean-project": "^4.0.1",
    "react-native-code-push": "^7.1.0",
    "react-native-device-info": "^8.7.1",
    "react-native-document-picker": "^8.1.3",
    "react-native-elements": "^3.4.2",
    "react-native-gesture-handler": "^2.6.2",
    "react-native-get-random-values": "~1.7.0",
    "react-native-image-picker": "^4.7.3",
    "react-native-image-viewing": "^0.2.1",
    "react-native-incall-manager": "^3.3.0",
    "react-native-logs": "^5.0.1",
    "react-native-permissions": "^3.1.0",
    "react-native-popup-menu": "^0.15.10",
    "react-native-reanimated": "^2.14.4",
    "react-native-safe-area-context": "3.2.0",
    "react-native-screens": "~3.4.0",
    "react-native-simple-toast": "^1.1.4",
    "react-native-snackbar": "^2.4.0",
    "react-native-sound": "^0.11.2",
    "react-native-splash-screen": "^3.3.0",
    "react-native-svg": "^12.3.0",
    "react-native-tab-view": "^2.15.2",
    "react-native-vector-icons": "^9.2.0",
    "react-native-voip-push-notification": "^3.3.0",
    "react-native-webrtc": "^106.0.5",
    "react-native-webview": "^11.26.1",
    "react-style-object-to-css": "^1.1.2",
    "socket.io-client": "^2.4.0",
    "sp-react-native-in-app-updates": "^1.2.0",
    "uuid": "^8.3.2",
    "validator": "^13.5.2"
  },
  "devDependencies": {
    "@babel/core": "^7.21.3",
    "@babel/runtime": "^7.21.0",
    "@react-native-community/eslint-config": "^2.0.0",
    "@tsconfig/react-native": "^2.0.2",
    "@types/jest": "^26.0.23",
    "@types/react": "^18.0.21",
    "@types/react-native": "^0.70.6",
    "@types/react-test-renderer": "^18.0.0",
    "@typescript-eslint/eslint-plugin": "^5.37.0",
    "@typescript-eslint/parser": "^5.37.0",
    "babel-jest": "^26.6.3",
    "babel-plugin-inline-import": "^3.0.0",
    "eslint": "^7.32.0",
    "jest": "^26.6.3",
    "metro-react-native-babel-preset": "0.72.3",
    "patch-package": "^6.5.1",
    "react-native-dotenv": "^3.4.7",
    "react-native-svg-transformer": "^1.0.0",
    "react-test-renderer": "18.1.0",
    "typescript": "^4.8.3"
  },
  "jest": {
    "preset": "react-native",
    "moduleFileExtensions": [
      "ts",
      "tsx",
      "js",
      "jsx",
      "json",
      "node"
    ]
  }
}

babel.config.js

module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: ['module:react-native-dotenv', 'react-native-reanimated/plugin'], // react-native-reanimated/plugin must be last
};

metro.config.js

const {getDefaultConfig} = require('metro-config');

module.exports = (async () => {
  const {
    resolver: {sourceExts, assetExts},
  } = await getDefaultConfig();
  return {
    transformer: {
      babelTransformerPath: require.resolve('react-native-svg-transformer'),
      getTransformOptions: async () => ({
        transform: {
          experimentalImportSupport: false,
          inlineRequires: false,
        },
      }),
    },
    resolver: {
      assetExts: assetExts.filter(ext => ext !== 'svg'),
      sourceExts: [...sourceExts, 'svg'],
    },
  };
})();

@MedulHasan
Copy link

I experience the same issue.
Is there any update or solution?

@Jamal-ReachFirst
Copy link

same here

@Amurmurmur
Copy link

api.cache(true)
thats what causes the caching, it has nothing to do with this lib. Its the fact that babel is caching it, so you can reset a cache on every start when you update your envs.
other libs like inline-env also have the same behaviour.
Hope it helps

@prageeth
Copy link

prageeth commented May 2, 2023

Having the same issue. Setting api.cache(false) doesn't solve it either.

@illusion77
Copy link

I come to my react native project root directory, run these command

rm -rf ~/Library/Developer/Xcode/DerivedData
xcrun simctl erase all
rm -rf node_modules
npm install
npx react-native start --reset-cache

it does work

@prateekuttreja2020
Copy link

I am having the same issue.
Is there any update or solution?

@MedulHasan
Copy link

I am having the same issue. Is there any update or solution?

In my case, using a single quote instead of the double quote when importing the 'env' variable does not cause this problem.
ex: import {BASE_API_URL} from '@env';

@winston-lee
Copy link

I am having the same issue. Is there any update or solution?

In my case, using a single quote instead of the double quote when importing the 'env' variable does not cause this problem. ex: import {BASE_API_URL} from '@env';

I don't think it does. Try changing the value in the variable and see if it picks it up.

@winston-lee
Copy link

winston-lee commented May 27, 2023

This is the one that worked for me #75 (comment)

In my package.json

"scripts": {
    "start:devmobile": "APP_ENV=devmobile expo start --clear",
}

And I ran

npm run start:devmobile

(P.S. i'm just starting out front end development so don't laugh at what i just did ;))

@philipostli
Copy link

I am having the same issue. Is there any update or solution?

In my case, using a single quote instead of the double quote when importing the 'env' variable does not cause this problem. ex: import {BASE_API_URL} from '@env';

This solved it for me. Says more about my experience with node than anything. Thank you for pointing this out!

@david-a
Copy link

david-a commented Jun 1, 2023

The problem in my case was Metro cache.
My solution was to add this line to the metro.config.js file:

module.exports = {
  ...
  cacheVersion: process.env.APP_ENV,
  ...
};

my package.json scripts are (always clearing cache for production runs) -

    "start": "react-native start",
    "start:staging": "APP_ENV=staging react-native start",
    "start:prod": "APP_ENV=prod react-native start --reset-cache",

according to the docs it's "an arbitrary string appended to all cache keys in the project before they are hashed". Subsequent runs of the same env keep the cache, while switching between them seems to invalidate it.

Hope it helps

@rob-johansen
Copy link

This is what worked for me in the metro.config.js file:

resetCache: true

@jfbaraky
Copy link

jfbaraky commented Jul 21, 2023

This is what worked for me in the metro.config.js file:

resetCache: true

Adding the resetCache: true to my metro.config.js worked. The final file is:

const { getDefaultConfig } = require('expo/metro-config');

module.exports = (() => {
  const config = getDefaultConfig(__dirname);

  const { transformer, resolver } = config;

  config.transformer = {
    ...transformer,
    babelTransformerPath: require.resolve('react-native-svg-transformer'),
  };
  config.resolver = {
    ...resolver,
    assetExts: resolver.assetExts.filter((ext) => ext !== 'svg'),
    sourceExts: [...resolver.sourceExts, 'svg'],
  };

  config.resetCache = true; // ADDED IT HERE <<<

  return config;
})();

But it was a nightmare until I found out about the caching. Probably this shouldn't be the default behaviour.

@o-alexandrov
Copy link

resetCache is a bad option to solve the caching issue, as you disable caching for everything.

Using cacheVersion described in detail in the prior comment is the only rational solution.

@jfbaraky
Copy link

The cacheVersion will only work if you manually set a new version every time you update your environment variables.

Having it only separated for each environment will make you face the same issues with the cache.

@o-alexandrov
Copy link

o-alexandrov commented Jul 21, 2023

You should concatenate all environment variables, so the cacheVersion changes together with any environment variable.

Example metro.config.js:

module.exports = {
  cacheVersion: [process.env.VAR1, process.env.VAR2, ].join(‘’)
}

@jfbaraky
Copy link

jfbaraky commented Jul 24, 2023

Shouldn't this be the default behavior?

At least for the development variables. Should they be cached at all?

@sanjarcode
Copy link

I can confirm this happens with React Native CLI too (i.e. react-native command)

@pauloguilherm
Copy link

npm start -- --reset-cache it worked for me

@LouisJS
Copy link

LouisJS commented Nov 14, 2023

Setting cacheVersion in metro.config.js doesn't reset cache when switch from one .env to another (or just any changes that happens in .env basically).

We want to repeat :
resetCache is a bad option to solve the caching issue, as you disable caching for everything.

You might wanna change your .env settings easily without manual deleting of cache. But still want to keep your cache when your working on the same env

@SulthanYS
Copy link

The problem in my case was Metro cache. My solution was to add this line to the metro.config.js file:

module.exports = {
  ...
  cacheVersion: process.env.APP_ENV,
  ...
};

my package.json scripts are (always clearing cache for production runs) -

    "start": "react-native start",
    "start:staging": "APP_ENV=staging react-native start",
    "start:prod": "APP_ENV=prod react-native start --reset-cache",

according to the docs it's "an arbitrary string appended to all cache keys in the project before they are hashed". Subsequent runs of the same env keep the cache, while switching between them seems to invalidate it.

Hope it helps

when I trying with this I got the following error only. even after I change in the metro.config.js

'APP_ENV' is not recognized as an internal or external command,
operable program or batch file.

@mofolo
Copy link

mofolo commented Jan 4, 2024

I'm only seemingly having this issue with my "Production" file.
Staging and Dev works, but the moment I set it to APP_ENV=Production it only reads from my .env.staging file.
However when I set the release to Debug (dev), it reads from my .env.development file without any issue.

@goatandsheep
Copy link
Owner

@mofolo this is a whole other issue unfortunately. If you're using production, you don't need APP_ENV, since NODE_ENV should already do it. Also, APP_ENV doesn't really work with production and development. Basically APP_ENV was introduced very late for people who were struggling to override NODE_ENV, but it has it's quirks so try to avoid using it where possible. Feel free to open a separate ticket on this if I'm wrong

@goatandsheep
Copy link
Owner

@mofolo actually it occurred to me, what if you try in the config setting the name for APP_ENV to something like REACT_APP_ENV

{
  "plugins": [
    ["module:react-native-dotenv", {
     "envName": "REACT_APP_ENV"
    }]
  ]
}

@goatandsheep
Copy link
Owner

goatandsheep commented Feb 9, 2024

@SulthanYS are you using Windows Powershell? to set environment variables in CLI, you need to do it differently

Set APP_ENV=test&& react-native start

@goatandsheep
Copy link
Owner

goatandsheep commented Feb 9, 2024

@jfbaraky you're right about the cache. it should be working and it isn't working. I just tested it now even with the package version resolutions. I worked with the babel team to try fix this. so i'm not sure why it's not working. Anyways, the solution is just to have to do api.cache(false) or --reset-cache every time you change your env type, or env values

@jovanialferez
Copy link

this works for me rm -rf node_modules/react-native-config/ios/ReactNativeConfig/GeneratedDotEnv.m

@Aixufey
Copy link

Aixufey commented May 5, 2024

Hi,

the module import with version 3.4.11 is breaking but I tried lower the version to 3.4.9 and only process.env. seems to work.
Could not resolve @env on launch.

@athrvk
Copy link

athrvk commented May 8, 2024

After scratching my head a lot the workaround i did is explained in this article

updating cacheVersion in metro.config.js did not work for me
so i used resetCache

and to update that i am calculating hash of env files and updating as required in a prestart script

since i am working on windows my solution is mainly for that but i think it can be used on other platforms

hope this helps!

prestart.mjs

import inquirer from 'inquirer';
import fs from 'fs';
import crypto from 'crypto'; // To calculate file hashes

const questions = [
    {
        type: 'list',
        name: 'environment',
        message: 'Which environment to run in?',
        choices: ['development', 'production'],
        default: 'development'
    }
];

inquirer.prompt(questions).then(async (answers) => {
    const env = answers.environment;
    const envFile = `.env.${env}`;

    try {
        // Check if the environment file exists
        if (!fs.existsSync(envFile)) {
            console.error(`Environment file ${envFile} not found.`);
            return;
        }

        // Calculate the hash of the environment file
        const fileHash = calculateFileHash(envFile);

        // Store previous state (if available)
        const prevStateFile = '.prestart.state.json';
        let prevState = {};
        if (fs.existsSync(prevStateFile)) {
            prevState = JSON.parse(fs.readFileSync(prevStateFile, 'utf8'));
        }

        // Check for changes
        const envChanged = prevState.env !== env;
        const fileChanged = prevState.fileHash !== fileHash;

        // Update .env file and set resetCache
        const shouldResetCache = envChanged || fileChanged;
        fs.writeFileSync('.env', `APP_ENV=${env}\nresetCache=${shouldResetCache}`);

        // Update the state file
        fs.writeFileSync(prevStateFile, JSON.stringify({ env, fileHash }));

        console.log(`Running in ${env} mode...`);

    } catch (error) {
        console.error('Error during setting environment:', error);
    }
});

function calculateFileHash(filename) {
    const fileBuffer = fs.readFileSync(filename);
    const hash = crypto.createHash('sha256');
    hash.update(fileBuffer);
    return hash.digest('hex');
}

metro.config.js

const { getDefaultConfig, mergeConfig } = require('@react-native/metro-config');
require('dotenv').config();

/**
 * Metro configuration
 * https://reactnative.dev/docs/metro
 *
 * @type {import('metro-config').MetroConfig}
 */
const config = {
    resetCache: process.env.resetCache === 'true',
};
module.exports = mergeConfig(getDefaultConfig(__dirname), config);

package.json

{
  ...,
  "scripts": {
    ...,
    "prestart": "node prestart.mjs",
    ...
  },
  ...
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests