Global Variables in ReactJS - reactjs

We're currently working on a school project and we have a part of the app that's kind of "external". (Just used for displaying a graph)
So that /graph folder is now in the /src folder and when compiling the react app we get tons of import errors.
Is there a way to bypass those imports and just use the global variables, which are already implemented?

Global variables in React can be implemented using webpack.config.js file.
Inside the webpack config file declare your global variable.
i.e. const API_URL = JSON.stringify('http://api:8080'); Also, you need to add new plugins to webpack, in order for this global variable to be accessible by your whole app.
new webpack.DefinePlugin({ //defining gloabl variable
API_URL,
});
But global variable aside if just want to get rid of the component and module import error, then you can simply set aliases for those in your .babelrc file. This way, you do not need to write '../../..something', you can just write 'something'.
{
"presets": ["react", "es2015", "stage-0"],
"plugins": [
["module-resolver", {
"root": ["./src"],
"alias": {
"src": "/",
"components": "/components",
"actions": "/actions",
"containers": "/containers",
"images": "/images",
"reducers": "/reducers",
"styles": "/styles",
"utils": "/utils",
}
}]
]
}}
This is a sample .babelrc file with alias for components and other folders. Now from any where in the app if I write import { Something } from 'components/Something', my app will know where to find it. No need to write '../../../components/Something'

It is not recomended to use global variables, but you can simply use window object to save variables, for example window.myGlobalVar = 3. In addition, you can use react context.

You can try this:
Parent.js
var myVar = {}
class MyComponent extends Component {
...
...
myVar = new Object();
...
...
render() {
return <div>
\\ children
<MyLeftBranch myVar={myVar} />
<MyRightBranch myVar={myVar} />
</div>
}
}
export default MyComponent;
child.js
class Child extends Component {
{ myVar } = this.props;
\\use myVar
}

Related

ReferenceError: React is not defined in jest tests

I have the following line that executes correctly in browser
eval(Babel.transform(template, { presets: ['react'] }).code);
but when I run jest tests I am getting ReferenceError: React is not defined
What am I missing?
More info:
in the test file I have the following:
const wrapper = shallow(<MyComponent/>);
const instance = wrapper.instance();
instance.componentFunction(...)
and then the componentFunction has the eval(Babel.transform(template, { presets: ['react'] }).code); line where template is something it gets from the test file and can be something like <span>...</span>
Please let me know if more details are needed
#babel/preset already has support for what you need. According to the react 17 documentation I only had to set the runtime to automatic in my babel.config.json.
{
"presets": [
["#babel/preset-react", {
"runtime": "automatic"
}]
]
}
If you are using #babel/plugin-transform-react-jsx the config should be
{
"plugins": [
["#babel/plugin-transform-react-jsx", {
"runtime": "automatic"
}]
]
}
The latter is usually not needed since #babel/preset-react includes #babel/plugin-transform-react-jsx.
Why you shouldn't use import React from 'react';
The documentation states:
There are some performance improvements and simplifications that React.createElement does not allow.
There is also a technical RFC that explains how the new transformation works.
If you want to upgrade. React also provides an automated script that removes unnecessarry imports from your code.
If you are using JSX in your jest test files, you will need to add the following import line to the top of the file:
import React from 'react';
The reason for this is that Babel transforms the JSX syntax into a series of React.createElement() calls, and if you fail to import React, those will fail.
The best solution for Next.js (where jsx: 'preserve' specifically is:
Configuring your babel config in the way that next already does: (no need to install another babel plugin):
babel.config.js:
module.exports = {
presets: ['next/babel']
};
Alternatively, if anyone experienced this bug had the problem wherein import React from 'react' is not necessary, because it is already included globally and doesn't need to be included in every file, then this solution may work for you.
I simply configured React to be globally defined in jest.
My jest.config.js:
module.exports = {
moduleDirectories: ['./node_modules', 'src'],
// other important stuff
setupFilesAfterEnv: ['<rootDir>/src/jest-setup.ts']
}
import '#testing-library/jest-dom';
import React from 'react';
global.React = React; // this also works for other globally available libraries
Now I don't need to worry about each file importing React (even though eslint knows that's unnecessary with Next.js)
This happened to me after react-native and jest and other node_modules related to unit test were upgraded.
Here is my working config:
jest.config.js
module.exports = {
preset: 'react-native',
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
moduleDirectories: ['./node_modules', 'src'],
cacheDirectory: '.jest/cache',
transformIgnorePatterns: [
'<rootDir>/node_modules/(?!#react-native|react-native)',
],
moduleNameMapper: {
'^[./a-zA-Z0-9$_-]+\\.svg$': '<rootDir>/tests/SvgStub.js'
},
setupFiles: ['./jest-setup.js'],
modulePathIgnorePatterns: ['<rootDir>/packages/'],
watchPathIgnorePatterns: ['<rootDir>/node_modules'],
}
For setup file I had to use project specific one ./jest-setup.js but for a general use './node_modules/react-native-gesture-handler/jestSetup.js' should work too.
babel.config
{
presets: ['module:metro-react-native-babel-preset'],
plugins: [
'react-native-reanimated/plugin'
]
};
Sources: https://github.com/facebook/jest/issues/11591#issuecomment-899508417
More info about the dependencies here: https://stackoverflow.com/a/74278326/1979861
If you are storing test data in separate test data files that contain JSX, you will also need to import react, as your file contains JSX and so results in a ReferenceError: React is not defined in jest tests error.
const testData = {
"userName": "Jane Bloggs",
"userId": 101,
"userDetailsLink": Jane Bloggs
};
importing react, as below, resolves the error.
import React from "react";
const testData = {
"userName": "Jane Bloggs",
"userId": 101,
"userDetailsLink": Jane Bloggs
};

How Can I Manually Mock Svg's in my Tests?

I'm using a stub file to mock images in my application, which works 99% of the time for me. However, I have a component that will render different images based on input, so I want to be able to check in my unit tests that the input creates the correct output.
Basically what I'm looking to do is if the user inputs "Lion", my component will display a picture of a lion, "Tiger a tiger, etc. Using moduleNameMapper, it's always test-file-stub and I want to be able to jest.mock('../lion.svg', ()=> 'lion.svg') for specific tests.
Thanks to Jest's transform config setting you may do that.
package.json
"jest": {
"transform": {
"\\.svg$": "<rootDir>/fileTransformer.js"
}
...
}
IMPORTANT
You need to explicitly provide transform to other extensions (especially *.js and *.jsx) otherwise you will get errors. So it should be something like:
"transform": {
"^.+\\.js$": "babel-jest",
"\\.svg$": "<rootDir>/fileTransformer.js"
...
}
As for fileTransformer.js it just emulates exporting file's path(you may add any transformation to strip the path or extension or whatever):
const path = require('path');
module.exports = {
process(src, filename) {
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
}
};
It means
import svgIcon from './moon.svg';
will work just like
const svgIcon = 'moon.svg'
So for component containing
...
<img src={svgIcon} />
you may write assertion like
expect(imgElementYouMayFind.props.src)
.toEqual('moon.svg')
Just a small addition to what #skyboyer suggest:
module.exports = {
process(src, filename) {
return `module.exports = ${JSON.stringify(path.basename(filename))}`;
}
};
instead you need have it like that:
module.exports = {
process(filename) {
return `module.exports = ${JSON.stringify(path.basename(filename))};`;
}
};
pay attention to ; after closing curly bracket.

Importing and running a standalone React application inside a web component

I have a standalone React application that uses webpack for bundling with a requirement of being able to run this bundle within a web component. Can anyone suggest how I should approach this?
I'm thinking something like:
//webpack.config.js
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
library: 'reactUmd',
libraryTarget: 'umd',
umdNamedDefine: true
},
//react-component.js
import '../../packages/react-umd/dist/bundle.js'
class ReactComponent extends HTMLElement {
connectedCallback() {
const mountPoint = document.createElement('span');
this.attachShadow({ mode: 'open' }).appendChild(mountPoint);
reactUmd.renderSomeComponentTo(mountPoint)
}
}
customElements.define('react-component', ReactComponent)
But I'm not sure how I can import the compiled bundle and get a reference to React, ReactDOM, the Main component etc, would exporting as UMD provide the references I need?
So you basically want to access the react component outside the minified bundle file.
Here is one approach:
You tell webpack to create a library for your file. This will basically create a global variable using which you can access all the exported functions from you js entry file.
To do so, add a key called library int your webpack config under output object.
module.exports = {
entry: './main.js',
output: {
library: 'someLibName'
},
...
}
After doing this, restart your webpack server and on console, type window.someLibName. This should print all the methods exported by main.js as an object.
Next step is to create a function which accepts a DOM element and renders the react component on the element. The function would look something like this:
export const renderSomeComponentTo = (mountNode) => {
return ReactDOM.render(<App />,MOUNT_NODE);
}
Now you can access the above function from anywhere in the project, using
const mountNode = document.getElementById('theNodeID');
window.someLibName.renderSomeComponentTo(mountNode);
This way, all the react specific code is abstracted :)
I hope I answered your question. Don't forget to hit star and upvote. Cheers 🍻

Babel plugin for react state outside of constructor

What babel plugin do i need to be able to use the following syntax?
class Temp extends React.Component {
---> state = {
test: '',
}
....rest
}
i have babel-core, babel-reset-env and babel-preset-react in my dev dependencies and my .babelrc has:
{
"presets": [
"env", "react"
]
}
While playing around on the babel site, i noticed that that syntax was being controlled by babel-stage-2. I couldn't find a reference to it in creat-react-app so i wanted to know how to actually enable that in my project.
Thanks for the help!
The plugin is called transform-class-properties.

Handlebars custom helper - migrating to Webpack

We have an app based on backbone, marionette and handlebars, without import/export or require methods, managed with grunt and we are trying to migrate to webpack.
I am having an issue with a custom helper for handlebars.
The code of our helper :
'use strict';
function I18n() {
this.constructor(arguments);
}
I18n.prototype = {
constructor: function () {
...some stuff
}
get: function () {
...some stuff
}
...some other functions
}
ourNameSpace.I18n = new I18n();
And it's included with this function in a file to load it globally :
Handlebars.registerHelper('i18n', _.bind(ourNameSpace.I18n.get, ourNameSpace.I18n));
Then we are using it in the template like this :
{{i18n "LblEmail"}}
I tried to use handlebars-loader and I added this query object into webpack.config to add it to the bundle :
{
test: /\.hbs$/,
use: {
loader: 'handlebars-loader',
query: {
helperDirs: [
path.resolve(__dirname, 'public/assets/js/common/i18n/')
]
}
}
}
Webpack add our helper code in the bundle, but when it's supposed to be called in the template I have this error :
Uncaught TypeError: __default(...).call is not a function
Webpack generated code of the bundle where is the call :
...
+ alias2(__default(__webpack_require__(2)).call(alias1,"LblEmail",{"name":"i18n","hash":{},"data":data}))
...
In a second time I also tried to add an export in the helper, even though we don't use the import/export method (yet) in our app. Adding this at the end of helper file :
export default I18n
That fix the error but the helper doesn't seem to work because all texts on the page are empty (instead of displaying i18n translation or keys)
Does someone did the same kind of migration with handlebars custom helper or would know how I can refactor that so Webpack can handle it properly and the bundle can execute it correctly ?
So after few months I will reply to my own question, I managed to fix our problem like this :
I rewrite our old legacy helper (with custom functions) by creating more modern ones (three, for our three functions that was in legacy helper) relying on I18nJS:
import I18nJs from 'i18n-js';
const I18n = key => I18nJs.t(key);
export default I18n;
It is loaded by webpack with handlebars loaders like this :
{
test: /\.hbs$/,
use: {
loader: 'handlebars-loader?runtime=handlebars/runtime',
query: {
helperDirs: [path.resolve(__dirname, 'src/js/common/i18n/helper')],
inlineRequires: '/images/',
precompileOptions: {
knownHelpersOnly: false,
},
},
},
}
And in our template we did not have to change anything to use it :
<label>{{i18n "LblEmail"}}</label>
To use localisation on javascript files however we had to make some changes :
I created a "helper" (not handlebar helper) implementing same logic than handlebars helper :
import I18nJs from 'i18n-js';
const I18n = {
get(key) {
return I18nJs.t(key);
},
... some other functions
};
export default I18n;
We import this file and use its function as usual in modern stacks :
import I18n from '../common/i18n/I18nSt';
...
console.log(I18n.get('PasswordMissing'));
So we had to do minor refactor when we call our translations function in our js files, It was like this before :
console.log(OurNamespace.I18n.get('PasswordMissing'));

Resources