How to handle IE11 compatibility on each dependent packages? - reactjs

I’m building an app which uses React(v.17.0), Next.js(v11.1.2), … and other stacks.
Next.js probably, in default, supports IE11.
After I added some packages to my project, it fails on IE11. It says, in console, spread operator is not supported and some other stuff.
I guess the reason is packages which occur problems doesn’t properly set up. For example, packages are built in typescript but they don’t specify tsconfig.compilerOptions.target to es5. I looked it up the source.
I have tried
Adding react-app-polyfill/ie11 in pages/_app.js
Should I try…
Contribute each packages so that it can be compatible with IE11.
Webpack or babel setting: I’ve never done custom webpack or babel settings before. I always go with the default settings. I haven’t tried this because I know webpack and babel affects project source but not sure it ALSO affects dependent packages.

As you mentioned, the spread operator does not work correctly in IE. This is because it is part of ES6 and IE does not support this feature. You can view the browser compatibility table from this doc:Spread syntax (...)-Browser compatibility.
So you may need to convert it to ES5, usually you can use transpilers like Babel, simple configuration example:
{
test: /\.js$/, //Regular expression
exclude: /(node_modules|bower_components)/,//excluded node_modules
use: {
loader: "babel-loader",
options: {
presets: ["#babel/preset-env"] //Preset used for env setup
}
}
}
Another option is to define your own function according to the situation.

Related

Ejecting from CRA to share components the shared directory are not linted

I'm experimenting with ejecting from CRA 4 app to enable simple sharing of react components among multiple modules in a monorepo:
|-react_project_a
|-react_project_b
|-shared
After ejecting, all I had to do is to add the shared module path to two places in the webpack.config file:
new ModuleScopePlugin(paths.appSrc, [
paths.appPackageJson,
paths.clientShared //*******here
reactRefreshOverlayEntry,
]),
and
test: /\.(js|mjs|jsx|ts|tsx)$/,
include: [paths.appSrc, paths.clientShared],
loader: require.resolve('babel-loader')
works a treat, I even get hot reload. The only problem is that only the main app gets linted and so compilation errors and warnings for the shared module appear only in the browser developer tool console, like this:
The EslintWebpackPlugin used by CRA doesn't support multiple contexts for the plugin. Is there an alternative? I'm trying to provide my developers with an experience as close as possible to what they had with CRA.
Since my goal was sharing jsx components among CRA/react apps, the way simpler solution was to create a symbolic link in each react project pointing to the shared directory:
|-react_project_a
|-shared=>shared
|-react_project_b
|-shared=>shared
|-shared
And to add this to the webpack.config
resolve: {
symlinks: false
}
Since this was the only change I required, I wound up using CRACO instead of ejecting to handle the override. I'd close the question, but think folks might find this useful.

Using components from an external directory in a Electron project with Webpack

I am trying to do this as simple as possible, I studied Yarn Workspaces for a while, but that's a solution that's currently doesn't work with Electron, there were simply too many issues.
I have am Electron project here: ./electron/
I have a directory with components here: ./common/
The components are developed in React/JSX, there is nothing really fancy about them. That said, I am using hooks (useXXX).
I tried many ways to include those components (ideally, I wanted to use Yarn Workspaces, but it only multiplied the number of issues), but they all failed. Which is why I would like to avoid using yarn link or workspaces or making the common a library, etc. I just want my Electron project to behave as if the files were under ./electron. That's it.
The closest I came to a solution is by using electron-webpack, and overriding it with this config:
module.exports = function(config) {
config = merge.smart(config, {
module: {
rules: [
{
test: /\.jsx?$/,
//include: /node_modules/,
include: Path.resolve(__dirname, '../common'),
loaders: ['react-hot-loader/webpack', 'babel-loader?presets[]=#babel/preset-react']
},
]
},
resolve: {
alias: {
'#common': Path.resolve(__dirname, '../common')
}
}
})
return config
}
I can import modules, and they work... except if I use hooks. And I am getting the "Invalid Hook Call Warning": https://reactjs.org/warnings/invalid-hook-call-warning.html.
I feel like that /common folder is not being compiled properly by babel, but the reality is that I have no idea where to look or what to try. I guess there is a solution for this, through that webpack config.
Thanks in advance for your help :)
I found the solution. That happens because the instance of React is different between /common and /electron.
The idea is to add an alias, like this:
'react': Path.resolve('./node_modules/react')
Of course, the same can be done for other modules which need to be exactly on the same instance. Don't hesitate to comment this if this answer it not perfectly right.
I wrestled more than a day with a similar problem. My project has a dependency on a module A that is itself bundled by Webpack (one that I authored myself). I externalised React from A (declaring it to be a commonjs2 module). This will exclude the React files from the library bundle.
My main program, running in the Electron Renderer process, uses React as well. I had Webpack include React into the bundle (no special configuration).
However, this produced the 'hooks' problem because of two instances of React in the runtime environment.
This is caused by these facts:
module A 'requires' React and this is resolved by the module system of Electron. So Electron takes React from node_modules;
the main program relies on the Webpack runtime to 'load' React from the bundle itself.
both Electron and the Webpack runtime have their own module cache...
My solution was to externalise React from the main program as well. This way, both the main program and module A get their React from Electron - a single instance in memory.
I tried any number of aliases, but that does not solve the problem as an alias only gives direction to the question of where to find the module code. It does nothing with respect to the problem of multiple module caches!
If you run into this problem with a module that you cannot control, find out if and how React is externalised. If it is not externalised, I think you cannot solve this problem in the context of Electron. If it is externalised as a global, put React into your .html file and make your main program depend on that as well.

GraphQL queries in Separate File (gql, graphql)

I am working on project with teachnology combination of React + Postgraphile (GraphQL) + axios(http request to postgraphile server).
It has lots of GraphQL queries. Initially started with queries in same file with the other JavaScript and rendering code but it became messy as soon as specific queries has been added.
While searching I came to know that we can detach queries into separate files - .graphql or .gql
For this to allow I have to integrate with Webpack module -
I wanted to know if there is simpler(kind of out of the box) way to achieve similar thing without using Webpack as it needs lots of configuration in place.
Any pointers or examples will be really helpful.
Thank you.
On the client, we typically create a mirrored tree of the pages directory under graphql then create js or ts files with the query exported! Then importing it where needed (in our case the graphql client request body).
So for example:
export const GET_TEAM_QUERY = `
query {
// your query here
}
`
Hope that helps!
Since it's non-standard functionality to import/require a file that's not .js or .json, you need some kind of plugin to tell the runtime how to interpret it and not crash. The two ways I know of are:
using the graphql-tag/loader in webpack, with only a single loader rule:
module.exports = {
// ...
module: {
rules: [
// babel loader, css imports,
{
test: /\.(graphql|gql)$/,
exclude: /node_modules/,
loader: 'graphql-tag/loader'
},
],
},
// ...
}
or, with babel-plugin-import-graphql in babel, though this plugin just mimcs the functionality of the above webpack loader, and in fact suggests using it along side graphql-tag to reduce the size of the compiled query. It is useful if you need to run your code with babel-node, but I would suggest the above webpack loader in most cases.
I found a very neat solution to this problem: graphql-code-generator. Using this tool, you can write your queries and fragments in separate files and it will compile them into typescript files, fully type-safe and ready to be imported into your components.

React Photoswipe using removed babel 5 option

I am just starting to dabble in react and one of the first components I want is something to use photoswipe.js. (react photoswipe) It looks like there is a pretty decent one on npm, but I am running into a problem. When I run my storybook to start testing and building my component, I get an error from babel. It says:
in ./~/react-photoswipe/lib/index.js
Module build failed: ReferenceError: [BABEL] C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\react-photoswipe\lib\index.js: Using removed Babel 5 option: C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\react-photoswipe\.babelrc.stage - Check out the corresponding stage-x presets http://babeljs.io/docs/plugins/#presets
at Logger.error (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\logger.js:41:11)
at OptionManager.mergeOptions (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\options\option-manager.js:220:20)
at OptionManager.init (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\options\option-manager.js:368:12)
at File.initOptions (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\index.js:212:65)
at new File (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\file\index.js:135:24)
at Pipeline.transform (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-core\lib\transformation\pipeline.js:46:16)
at transpile (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-loader\lib\index.js:46:20)
at C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-loader\lib\fs-cache.js:79:18
at ReadFileContext.callback (C:\Code\GIT\DanStatenReact\DanStatenUI\node_modules\babel-loader\lib\fs-cache.js:15:14)
at FSReqWrap.readFileAfterOpen [as oncomplete] (fs.js:365:13)
So I did a bit of poking around and noticed that the babel rc file appears to be set to stage:0 which from my understanding seems like a really bad idea if you are producing a component that is supposed to be durable as the javascript spec updates and evolves.
I am still pretty new to using babel though so I am kind of having a hard time tracking down what I would need to update for this component to get it working in my environment with the newer babel. Has anyone encountered this problem with this component before? Does anyone have any advice or tips on how to troubleshoot the bable transpile and track down what I need to update?
The .babelrc from react-photoswipe does not work with babel 6. But it doesn't need to, because main entry point of the module is lib/index.js, which contains the already transpiled code. You're trying to transpile it again, and it automatically applies the .babelrc closest to it.
You should exclude node_modules in your webpack config, for example:
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
}
It will not only fix your issue, but also reduce the build time.
Thanks Michael for getting me pointed in the right direction. I am testing and building a component using a react storybook tool that has a whitelist configuration that tells it what node modules not to run through the full build. I had to add react-photoswipe to that whitelist and it is now working... well starting to work, but this problem is taken care of.

How to polyfill Array.prototype.find using webpack ProvidePlugin?

I'm using webpack and have followed examples to polyfill fetch and Promise for older browsers based on the whatwg-fetch and and es6-promise packages respectively.
new webpack.ProvidePlugin({
'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch',
'Promise': 'exports?global.Promise!es6-promise'
}),
This all makes sense however now I need to polyfill Array.prototype.find() but can no find of how to achieve this using webpack's ProvidePlugin feature. The thing that makes find different is that it is essentially a method on the Array prototype and I haven't found any examples of how to specify such things using ProvidePlugin. Has anybody used webpack to polyfill ES6 array functions like these? Any pointers on how this can be achieved? Am I going about this the wrong/outdated way or is there a better way to achieve what I need?
So far I've tried experimenting around with the syntax using this polyfill package from paulmillr but haven't had success using it with ProvidePlugin.
UPDATES
Doing more research on this lead me to babel polyfill. And these resources:
Why does Object.assign() require a polyfill when babel-loader is being used?
Babel polyfill? What is that?
Getting Babel 6 to work with IE8 (via. Gulp/Webpack)
I've found also here that the window.fetch polyfill is missing from babel-polyfill which explains why it's perhaps often a special case handled by ProvidePlugin. I'm also piecing together that ProvidePlugin is more of a convenience tool than a general tool for applying polyfills. Directly importing babel-polyfill and removing Promise and other ES6 pollyfills except for fetch is showing to be somewhat promising in my experiments so far.
What I've settled on for now is the following solution.
In the root of the application import/require babel-polyfill for general ES6 polyfills such as Array.prototype.find, Object.assign, and Promise. Since fetch is a special case, because it is viewed as not suitable for all environments, a separate polyfill is included for whatwg-fetch.
import "babel-polyfill";
import "whatwg-fetch";
In the webpack configuration remove any ES6 features provided through the ProvidePlugin but leave any other conveniences there (ex. JQuery orz).
new webpack.ProvidePlugin({
$: "jquery",
jQuery: "jquery",
// 'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch',
// 'Promise': 'exports?global.Promise!es6-promise',
}),
This should give me more rounded ES6 support with manually opting in for each feature I make use of. My hope is that Babel 5/6 will be able to Tree-Shake any unused feature that may otherwise cause bloat by including all of babel-polyfill, perhaps somebody else can speak to that.

Resources