React and environment variables - reactjs

I'm currently a bit confused about react and env variables. Basically, what I would like to achieve is, to have different files. Something like: enviorment.dev.js, enviorment.prod.js.
I couldn't find the documentation and there seem to be a lot of different options to choose from.
I guess, I look for something like this: https://medium.com/beautiful-angular/angular-2-and-environment-variables-59c57ba643be just for react.

If you're using webpack, there's a plugin, but that's the sort of place where you would need to configure that. As Felix said, React doesn't do that:
https://webpack.js.org/plugins/define-plugin/
webpack.config:
new webpack.DefinePlugin({
PRODUCTION: JSON.stringify(true),
VERSION: JSON.stringify("5fa3b9"),
BROWSER_SUPPORTS_HTML5: true,
TWO: "1+1",
"typeof window": JSON.stringify("object")
})
index.js:
if (!PRODUCTION) {
console.log('Debug info')
}
if (PRODUCTION) {
console.log('Production log')
}

Related

Backstage - How to consume environment variables?

The Backstage documentation states that all environment variables must be "exposed" through the central configuration file, app-config.yaml.
However, the official documentation is not clear about the use of these variables, for example, in the .ts and .tsx files.
Could someone help, or exemplify with codes how this use is made?
Had the same question, ended up just using process.env.ENV_VAR in the typescript files and exporting environment variables before starting Backstage.
There's a standard configuration API for both frontend and backend plugins or codes. An API reference can be found here.
You can try something like:
import { Config } from '#backstage/config';
interface IBackendConfig {
KEY_1: string;
KEY_2: string;
KEY_3: string;
}
const getBackendConfig = (config: Config): IBackendConfig => {
return config.get<IBackendConfig>('backend.env');
}
In your app-config.yaml
backend:
env:
KEY_1: "value1"
KEY_2: "value2"
KEY_3: "value3"
Note: Because of this syntax, configuration keys cannot contain dots.
Another option for accessing the env value is to create a sub-view of the configuration,
config.getConfig('backend').getString('env').

React component library can not load its own static asset

I have a react app (using create react app) and then a component library (using webpack directly). One component in the component library needs to load a png file in one of the components that it exports. In the webpack config for the component library I have a section such as:
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
This successfully results in a file like: 29b6b66cf1a691be2b3f.png in the component libraries output directory. The issue, is that when the application uses that component and the component attempts to load the image, the img element is <img ... src="29b6b66cf1a691be2b3f.png" /> and that fails to load, since that image actually lives in the component library folder (within the react application's node_modules/component-library/ at that point).
I have scoured the internet to the best of my ability, and can not seem to figure out what the best solution would be here. Any help is appreciated. I will quickly offer clarification if needed.
UPDATE: I have discovered CopyWebpackPlugin but it is quite unfortunate that this would require me to eject the "parent" application from create react app. Something I would very much prefer to avoid.
UPDATE2: Current plan is to try following something like what is explained here. The jist of it is to utilize something like rewire to avoid needing to eject and still be able to edit the webpack config via something like:
// in ./build.js
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
// edit webpack config values here
... I will answer my own question here if I find that this approach works.
Alright, well this felt like it was much messier than it needed to be, but I was able to figure out a way to accomplish this. The pieces were as follows.
First, needed to install copy-webpack-plugin. This was not as simple as it might sound, because I needed to find one that would not conflict with the version of webpack required by my react-scripts (create react app). I determined that copy-webpack-plugin#6.4.1 was the last version to support webpack v4, so I installed that and then added the following to my package.json:
"overrides": {
"copy-webpack-plugin": {
"webpack": "4.44.2"
}
},
this ensured that it would use the version of webpack that my react-scripts installation was expecting.
Then, I also needed to install rewire, and add the following to a file called build.js:
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
const CopyPlugin = require("copy-webpack-plugin");
const patterns = [
{
context: "node_modules/component-library/path/",
from: "*.png",
to: "."
},
]
if(config.plugins === undefined){
config.plugins = [new CopyPlugin({patterns})]
}else{
config.plugins.push(new CopyPlugin({patterns}))
}
// below lets it work in dev mode too
if(config.devServer === undefined){
config.devServer = {
writeToDisk: true
}
}else{
config.devServer.writeToDisk = true;
}
Finally, had to update my build script to be:
"scripts": {
...
"build": "node ./build.js",
...
},

Customizing antd Less or Css for create-react-app

I am using create-react-app as starter kit and was having a go at antd component library.
I can't seem to find a way to customize their styles (for branding purpose). Documentation doesn't seem to give clear directions regarding customization.
Docs link: https://ant.design/docs/react/customize-theme
They suggest two ways to do it though :
1.Using theme property . But this only works for antd-init or dva-cli boilerplates and not for create-react-app
2.Overriding Less variables .
Now to make the either of these option work for create-react-app without eject , what are the steps I need to take?
You question is not very well researched I am afraid. At a minimum, read the obvious resurces like the instructions at https://ant.design/docs/react/use-with-create-react-app and older Stackoverflow questions (like How do I use .babelrc to get babel-plugin-import working for antd?) before posting a question.
As it stands, CRA does not support changing the Babel configuration. This is needed to include the antd babel-plugin, which is needed to support importing only the necessary antd modules.
As a result you can only import the whole monolithic antd in a non-ejected CRA app.
if you don't intend to use eject you should use config-overrides.js to specify how you want to modify the default webpack.config.js that is part of react-scripts. Then once you have a config-overrides you can add a less Rule that has an option for modifyVars. Here you can specify any changes to the less variables.
function addLessRule(config, env)
{
const { getLoader, loaderNameMatches } = require("react-app-rewired");
const fileLoader = getLoader(
config.module.rules,
rule => loaderNameMatches(rule, 'file-loader')
);
const lessExtension = /\.less$/;
fileLoader.exclude.push(lessExtension);
const cssRules = getLoader(
config.module.rules,
rule => String(rule.test) === String(/\.css$/)
);
var lessModifyVars = {
'primary-color': '#990000',
'link-color': '#1DA57A',
'border-radius-base': '2px',
'layout-header-background' : '#f0f2f5'
};
var lessLoaders = [
{ loader: 'style-loader'} ,
{ loader: 'css-loader' },
{ loader: 'less-loader', options: {
modifyVars: lessModifyVars
} }
];
lessRule = {
test : lessExtension,
use: lessLoaders,
}
config.module["rules"].push( lessRule );
return config
}
If you want to customize antd without using react eject and don't want to change use forked versions of create-react-app or react scripts then
you can simply create a less file (let's say main.less ) , import antd.less and replace the default variables of ant design in this main.less file.
Now compile this less file using lessc (npm i -g less).
lessc "main.less antd.css" --js
--js is for inline javascript in less
Now simply include these complied css in your app.
take a look at https://medium.com/#aksteps/782c53cbc03b

Webpack resolve index.whatever.jsx automatically

When using webpack to resolve my imports:
/component/thing/index.jsx
/component/stuff/index.jsx
can be imported like
import Thing from './component/thing';
import Stuff from './component/stuff';
because index.jsx is resolved automatically. Trouble is now I've got a large number of files named index.jsx in my project making searching for file difficult and annoying. Is there a way to configure webpack to accept something like:
/component/thing/thing.index.jsx
/component/stuff/stuff.index.jsx
but still allow shorthand imports like above?
My first instinct was to (in Webpack):
resolve: {
extensions: ['.index.jsx']
}
but that didn't resolve. Is it possible to do what I'm looking for?
This resolver plugin might help you.
I haven't tried it. But according to the description, you should be able to do something like this.
resolve: {
plugins: [
new DirectoryNamedWebpackPlugin({
transformFn: function(dirName) {
// use this function to provide custom transforms of resolving directory name
// return desired filename or array of filenames which will be used
// one by one (honoring order) in attempts to resolve module
return [dirName + ".index", dirName + "/" + dirName + ".index"]
}
})
]
}
I'm not sure how this transformFn works and that's why I return two possible paths as an array. Do some experiment with it and you will be able to find the correct path to return in your case.
Also, ensure that you that you are using the correct version of the plugin based on your webpack version.
Do you have to use the folder/index.js structure? You could just have thing.js live in the /component folder so then you could use webpack resolve to import things in shorthand like you have above:
import Thing from 'component/thing';
import Stuff from 'component/stuff';
The webpack resolve would look something like this:
resolve: {
modules: [__dirname, 'node_modules'],
extensions: ['.js']
}

Setting global variables in webpack for front-end config?

I have a different URL for our api depending if it's development or production for a react app.
Using webpack, how can I set an env var like __API_URL__ and then change that depending if it's built using webpack.config.dev vs webpack.config.prod
I thought the answer may be in webpack.DefinePlugin but no luck.
new webpack.DefinePlugin({
__API_URL__: 'localhost:3005',
}),
I'd expect __API_URL__ to be available as a global but no such luck.
What would be the right way to do this? Also key thing is that no express server on the prod deploy. So this has to happen during the build...
As Michael Rasoahaingo said, the DefinePlugin works similar like replacing values with regular expressions: It replaces the value literally in your source code. I would not recommend to use the DefinePlugin for this kind of task.
If you want to switch configs based on the environment, you could use resolve.alias for that. Just import your config like this:
var config = require("config");
and then add a mapping in your webpack.config.js:
resolve: {
alias: {
config$: require.resolve("path/to/real/config/file")
}
}
DefinePlugin is not working as you expected. It doesn't expose __API_URL__ as a global variable.
According to the documentation: "The values will be inlined into the code which allows a minification pass to remove the redundant conditional."
So, it will find all occurence of __API_URL__ and changes it.
var apiUrl = __API_URL__
and
__API_URL__: '"localhost:3005"' // note the ' and "
become
var apiUrl = "localhost:3005"

Resources