How use redux without requirejs? - javascript

I'm trying to use Redux with reactjs and it's failing with the error:
Uncaught ReferenceError: require is not defined
I'm trying to include react and redux like this:
In index.html head tag
<script src="https://npmcdn.com/react-dom#15.3.0/dist/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/babel-core/5.6.16/browser.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.2/jquery.min.js"></script>
<script src="/redux.min.js"></script>
<script src="/react-redux.min.js"></script>
But when I do this (from the redux website):
import { createStore } from 'redux';
I get the error and if I don't have that statement, then I get an error that createStore doesn't exist when I try to use it.
How can I use redux + react without requirejs?

The website is using ES6 syntax, which is not implemented in major browsers today. You need to add a compile step using babel, which would output all of your javascript into one bundle.js file, including your code, react, redux, and all other dependencies.
if you want to work with react and redux, it makes more sense to learn how to set up a workflow to use ES6 syntax. You could consider using create-react-app as a base, and add redux support from there:
npm install -g create-react-app
create-react-app newReduxApp
npm install --save redux
npm start
Then head into App.js and add the createStore. Because create-react-app has set up ES6 compilation for you using babel, you will be able to use ES6 syntax.
If you would prefer to not use ES6 syntax, you won't be able to use a lot of the documentation on the web without first changing it to ES5 syntax. Using http://redux.js.org/docs/api/ as a reference, your line would be:
var createStore = Redux.createStore

You should add a browserify grunt task to bundle the compiled js by babel. browserify contains a small CommonJS require module, which does the required method.
Here is my grunt task for browserify:
module.exports = function(grunt) {
var files = {
'.tmp/public/js/z/bundle-<%= githash.main.hash %>.js': ['assets/js/**/*.jsx']
};
grunt.config.set('browserify', {
dist: {
options: {
transform: [['babelify', {presets: ['airbnb']}]]
},
files: files,
},
});
grunt.loadNpmTasks('grunt-browserify');
};
Besides, you should add following dev dependencies to you package.json.
"devDependencies": {
"babel-preset-airbnb": "^2.1.1",
"babel-preset-es2015": "^6.16.0",
"babel-preset-react": "^6.16.0",
"babelify": "^7.3.0",
"grunt-browserify": "^5.0.0"
}

Related

Custom React Component Library - jest 'cannot find module react'- testing-library, rollup

I'm building a custom react component library to share with other applications. I'm using rollup and following this blog and a few others: https://dev.to/alexeagleson/how-to-create-and-publish-a-react-component-library-2oe
The relevant snippet of my rollup config is as follows:
export default [
{
input: "src/index.ts",
output: [ { file: packageJson.main, format: "esm", sourcemap: true } ],
plugins: [ peerDepsExternal(), resolve(), terser() ],
external: ["react", "react-dom"]
}
]
In my package.json I've moved react and react-dom from 'devDependencies' to 'peerDependencies'. My abbreviated package.json:
{
"name": "custom components",
"version": "1",
"devDependencies": {
...stuff, (but not react/react-dom)
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
},
"main": "dist/esm/index.js",
"files": ["dist"]
}
Then using npm link I've imported my component library into another app which is using CRA and react-testing-library.
So far this works. I'm able to render my common components as expected.
However it seems like moving react/react-dom out of devDependencies is making my jest tests fail in both projects. In both cases jest cannot find react. The Jest output in my CRA that imports the components is as follows:
Test suite failed to run
Cannot find module 'react' from 'index.js' //<-- this is the index of the component library
However, Jest was able to find:
.../MyComponent.tsx
You might want to include a file extension in your import, or update your 'moduleFileExtensions', which is currently ['web.js',...etc] (defaults)
And when I run my tests in my common component library:
Test suite failed to run
Cannot find module 'react' from 'MyComponent'
import React from 'react';
^
I'm not sure how exactly to get around this. If I move react/react-dom back to devDependencies, the tests will run successfully, but then any component using react state will fail to render due to multiple copies of React in the project.
Could this be an issue because I'm using npm link as opposed to actually publishing the application to npm and installing or is this an issue with my rollup config/jest config/package.json? Any help would be appreciated.
react and react-dom should be included in both devDependencies and peerDependencies of your library.
Including them in devDependencies makes them available when developing (and when running tests), but they won't be included in the library when you bundle them with Rollup (which is what you want).
Including them in peerDependencies tells any consuming applications "you must have these dependencies at the specified version range," but again, Rollup doesn't include them in the library bundle.
If you only include them in peerDependencies, they aren't installed when developing the library or running tests, although this might no longer be true if you're using npm v7.
You can run into the problem of having multiple versions of React in your project if your app includes them at a different version than your library, but if both the app and the library have the same version range you shouldn't have that problem.

Solve having more than one copy of React in the same app

I'm developing a React module locally. For that, I'm linking my module using npm link.
The module is imported successfully but hooks are failing inside the module. It's throwing the following error:
Invalid hook call. Hooks can only be called inside of the body of a
function component. This could happen for one of the following
reasons: 1. You might have mismatching versions of React and the
renderer (such as React DOM) 2. You might be breaking the Rules of
Hooks 3. You might have more than one copy of React in the same app
See https://reactjs.org/link/invalid-hook-call for tips about how to
debug and fix this problem.
Checking the suggestions at React docs, I can confirm my app is using duplicate versions of React since the following code returns false:
// node_modules/mymodule/src/index.js
export { default as ReactFromModule } from 'react'
// src/index.js
import React from 'react'
import { ReactFromModule } from 'mymodule'
console.log(React === ReactFromModule) //false
This issue is full of suggestions but they are confusing. How can I solve it?
Note: Im not breaking rules of hooks, the error appears only when importing the module from an application.
In the module you are developing, add the conflicting packages to peerDependencies (and remove them from dependencies or devDependencies):
// package.json
"peerDependencies": {
"react": "16.13.1",
"react-dom": "16.13.1"
},
Execute npm install in your module.
Now add react and react-dom to the webpack configuration of your module as externals. These packages shouldnt be included in the bundle of the module (the app that uses the module will provide them):
// webpack.config.js
module.exports = {
/*
rest of config...
*/
output: {
filename: "index.js",
pathinfo: false,
libraryTarget: 'umd', // In my case, I use libraryTarget as 'umd'. Not sure if relevant
},
externals: {
// Use external version of React
"react": {
"commonjs": "react",
"commonjs2": "react",
"amd": "react",
"root": "React"
},
"react-dom": {
"commonjs": "react-dom",
"commonjs2": "react-dom",
"amd": "react-dom",
"root": "ReactDOM"
}
},
};
Then, after building your module, in your application you can check that both versions are now the same:
// node_modules/mymodule/src/index.js
export { default as ReactFromModule } from 'react'
// src/index.js
import React from 'react'
import { ReactFromModule } from 'mymodule'
console.log(React === ReactFromModule) // true :)
Adding react and react-dom as peerDependencies in the package.json didn't work for me.
I had to add an alias to the webpack configuration file:
// webpack.config.js
resolve: {
alias: {
react: path.resolve('./node_modules/react'),
}
In response to another comment, merely moving React to peerDependencies does not adequately resolve the issue in all cases. I would reply to that comment directly, but StackOverflow requires more reputation to respond to wrong answers than it does to post them.
I have a shared React component module built using Webpack and have run into the same issue. I've outlined one possible fix in this comment below which requires modifying peerDependencies and using npm link in a fashion similar to the answer shared by mtkopone.
https://github.com/facebook/react/issues/13991#issuecomment-841509933
My solution is a bit hacky and I wouldn't recommend it for long-term use. If you are using Webpack (which you tagged this question as), this article may detail a more permanent solution (https://medium.com/codex/duplicate-copy-of-react-errors-when-using-npm-link-e5011de0995d). I haven't tried it yet, but the author seems to have tried all the (incorrect) solutions out there and is also running into the hooks issue while trying to build shared component libraries.
The author of that article is trying to debug a Create-React-App app. While CRA uses webpack under the hood, you can't access the webpack.config directly, so the author has to perform some workarounds to do so. If you aren't using CRA, but just plain Webpack, then you could consider using the resolve.alias section of webpack.config to ensure there are no duplicate copies of React (see: https://blog.maximeheckel.com/posts/duplicate-dependencies-npm-link/)
I was attempting to use the peerDependencies and removal of the devDependencies and it was failing.
It turned out I had a node_modules folder in one of the parent folders of the library I was working on and the duplicate version of React was being loaded from there instead of the tool that was trying to use the React library.
Rather than editing the devDependencies to remove react I just wrote a small script to delete anything that's in the peerDependencies from the node_modules folder.
npm view --json=true . peerDependencies | jq -r 'keys | .[] | #text' | while read dep; do rm -r ./node_modules/${dep} && echo Removed ${dep}; done
In my case I was also missing import React from 'react' from couple of files.
check this

Don't include lodash in production build, but use it when single file is imported

I'm writing my own JS library of helpers that I need in my projects.
https://github.com/kitze/kitze-js-helpers
In the projects where I'm using ES6 and Babel I just want to import some of the helpers like
import {getPropertyFromAnotherArray} from 'kitze-js-helpers/src/helpers/array-helpers';
But I'm getting this error array-helpers.js:66 Uncaught TypeError: _lodash2.default.map is not a function
In the package.json of my library I have this:
"browser": {
"lodash": false,
"atob": false,
"btoa": false
},
"browserify": {
"transform": [
"babelify",
"browserify-shim"
]
},
"browserify-shim": {
"lodash": "global:_"
}
The error goes away if I remove those properties from package.json, but then lodash, atob, and btoa will get bundled along with my helpers in the dist/ folder.
At the top of array-helpers.js I have
import _ from 'lodash'
So the behaviour that I want is:
If the library is used as a bundled/minified version I would
like lodash not to be bundled inside with the helpers, but to be used
from window._ , so lodash must be included with a
before the helpers.
If the library is used in ES6 environment and only one file is
included, lodash should be used from the node_modules folder of the current project where the helpers are included.
When building a bundled/minified version of the library lodash should be ignored and not included in the final build.
Any help?

How Prevent Multiple Copies Of React from Loading?

In my previous Meteor app, using browserify, and React, all was working until I switched to meteor webpack.
I use react-select in my Meteor apps and it worked great but with browserify I could prevent multiple copies of react from loading which prevents this error I'm now having:
Error: Invariant Violation: addComponentAsRefTo(...): Only a ReactOwner can have refs. You might be adding a ref to a component that was not created inside a component's `render` method, or you have multiple copies of React loaded (details: https://fb.me/react-refs-must-have-owner).
My package.json look this:
...
"dependencies": {
"classnames": "^2.1.3",
"lodash": "^3.10.0",
"react": "^0.14.6",
"react-dom": "^0.14.6",
"react-mixin": "^2.0.1",
"react-select": "^1.0.0-beta8"
},
...
Is there a configuration in webpack I could use something call externals? Not fully sure what that means but a comment said to use:
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}
Since you use webpack, you can add an alias for loading react, like this:
// In webpack.config.js
resolve: {
alias: {
react: path.resolve('node_modules/react'),
},
},
This prevented the addComponentAsRefTo(...) error and made our build succeed again. However, for some reason, the testing build failed only on our CI environment as it could not resolve the node_modules/react path. I think it's unlikely that you will encounter this particular problem, though.
Something that worked for me was:
Uninstall all globally installed packages related to react (create-react-app, react-native, react and so on)
then: rm -rf node_modules
then: use npm install instead of yarn install
considering an App created with crate-react-app and ejected
In my case, I was building a separate npm module, then including that as a library in another project locally using npm link ../some-library. One of the modules within that library caused this error when I ran the parent project.
When I ran into this error, the solution for me was to prevent react and react-dom from being including in the some-library bundle output, by adding the following to the module.exports of its webpack.config.js:
externals: {
react: 'react',
'react-dom': 'react-dom'
}
If You use web pack then you can fix it by adding the following to Webpack config
for Don't bundle react or react-dom
externals: {
'react': 'React',
'react-dom': 'ReactDOM'
}

How do I install the babel-polyfill library?

I just started to use Babel to compile my ES6 javascript code into ES5. When I start to use Promises it looks like it's not working. The Babel website states support for promises via polyfills.
Without any luck, I tried to add:
require("babel/polyfill");
or
import * as p from "babel/polyfill";
With that I'll get the following error on my app bootstrapping:
Cannot find module 'babel/polyfill'
I searched for the module but it seems I'm missing some fundamental thing here. I also tried to add the old and good bluebird NPM but it looks like it's not working.
How to use the polyfills from Babel?
This changed a bit in babel v6.
From the docs:
The polyfill will emulate a full ES6 environment. This polyfill is automatically loaded when using babel-node.
Installation:
$ npm install babel-polyfill
Usage in Node / Browserify / Webpack:
To include the polyfill you need to require it at the top of the entry point to your application.
require("babel-polyfill");
Usage in Browser:
Available from the dist/polyfill.js file within a babel-polyfill npm release. This needs to be included before all your compiled Babel code. You can either prepend it to your compiled code or include it in a <script> before it.
NOTE: Do not require this via browserify etc, use babel-polyfill.
The Babel docs describe this pretty concisely:
Babel includes a polyfill that includes a custom regenerator runtime
and core.js.
This will emulate a full ES6 environment. This polyfill is
automatically loaded when using babel-node and babel/register.
Make sure you require it at the entry-point to your application, before anything else is called. If you're using a tool like webpack, that becomes pretty simple (you can tell webpack to include it in the bundle).
If you're using a tool like gulp-babel or babel-loader, you need to also install the babel package itself to use the polyfill.
Also note that for modules that affect the global scope (polyfills and the like), you can use a terse import to avoid having unused variables in your module:
import 'babel/polyfill';
For Babel version 7, if your are using #babel/preset-env, to include polyfill all you have to do is add a flag 'useBuiltIns' with the value of 'usage' in your babel configuration. There is no need to require or import polyfill at the entry point of your App.
With this flag specified, babel#7 will optimize and only include the polyfills you needs.
To use this flag, after installation:
npm install --save-dev #babel/core #babel/cli #babel/preset-env
npm install --save #babel/polyfill
Simply add the flag:
useBuiltIns: "usage"
to your babel configuration file called "babel.config.js" (also new to Babel#7), under the "#babel/env" section:
// file: babel.config.js
module.exports = () => {
const presets = [
[
"#babel/env",
{
targets: { /* your targeted browser */ },
useBuiltIns: "usage" // <-----------------*** add this
}
]
];
return { presets };
};
Reference:
usage#polyfill
babel-polyfill#usage-in-node-browserify-webpack
babel-preset-env#usebuiltins
Update Aug 2019:
With the release of Babel 7.4.0 (March 19, 2019) #babel/polyfill is deprecated. Instead of installing #babe/polyfill, you will install core-js:
npm install --save core-js#3
A new entry corejs is added to your babel.config.js
// file: babel.config.js
module.exports = () => {
const presets = [
[
"#babel/env",
{
targets: { /* your targeted browser */ },
useBuiltIns: "usage",
corejs: 3 // <----- specify version of corejs used
}
]
];
return { presets };
};
see example: https://github.com/ApolloTang/stackoverflow-eg--babel-v7.4.0-polyfill-w-core-v3
Reference:
7.4.0 Released: core-js 3, static private methods and partial
application
core-js#3, babel and a look into the future
If your package.json looks something like the following:
...
"devDependencies": {
"babel": "^6.5.2",
"babel-eslint": "^6.0.4",
"babel-polyfill": "^6.8.0",
"babel-preset-es2015": "^6.6.0",
"babelify": "^7.3.0",
...
And you get the Cannot find module 'babel/polyfill' error message, then you probably just need to change your import statement FROM:
import "babel/polyfill";
TO:
import "babel-polyfill";
And make sure it comes before any other import statement (not necessarily at the entry point of your application).
Reference: https://babeljs.io/docs/usage/polyfill/
First off, the obvious answer that no one has provided, you need to install Babel into your application:
npm install babel --save
(or babel-core if you instead want to require('babel-core/polyfill')).
Aside from that, I have a grunt task to transpile my es6 and jsx as a build step (i.e. I don't want to use babel/register, which is why I am trying to use babel/polyfill directly in the first place), so I'd like to put more emphasis on this part of #ssube's answer:
Make sure you require it at the entry-point to your application,
before anything else is called
I ran into some weird issue where I was trying to require babel/polyfill from some shared environment startup file and I got the error the user referenced - I think it might have had something to do with how babel orders imports versus requires but I'm unable to reproduce now. Anyway, moving import 'babel/polyfill' as the first line in both my client and server startup scripts fixed the problem.
Note that if you instead want to use require('babel/polyfill') I would make sure all your other module loader statements are also requires and not use imports - avoid mixing the two. In other words, if you have any import statements in your startup script, make import babel/polyfill the first line in your script rather than require('babel/polyfill').
babel-polyfill allows you to use the full set of ES6 features beyond
syntax changes. This includes features such as new built-in objects
like Promises and WeakMap, as well as new static methods like
Array.from or Object.assign.
Without babel-polyfill, babel only allows you to use features like
arrow functions, destructuring, default arguments, and other
syntax-specific features introduced in ES6.
https://www.quora.com/What-does-babel-polyfill-do
https://hackernoon.com/polyfills-everything-you-ever-wanted-to-know-or-maybe-a-bit-less-7c8de164e423
Like Babel says in the docs, for Babel > 7.4.0 the module #babel/polyfill is deprecated, so it's recommended to use directly core-js and regenerator-runtime libraries that before were included in #babel/polyfill.
So this worked for me:
npm install --save core-js#3.6.5
npm install regenerator-runtime
then add to the very top of your initial js file:
import 'core-js/stable';
import 'regenerator-runtime/runtime';

Categories

Resources