I am beginner in React and I am struggling with compiler error. Let me introduce my situation. I have two independent React applications:
App A - Big ERP
App B - "Plugin" to the App A
I supposed I will develop App B as an independent application. Then, I will install it to the App A (using npm install git#github.repo/...) once I finish development of the App B. I expected I will call components from App B within the App A source code. Everything went fine until I run the compilation. I am receiving:
SyntaxError: /frontend/node_modules/connector_frontend/src/views/Connector/FormView/index.js: Unexpected token
In my /frontend/node_modules/connector_frontend/src/views/Connector/FormView/index.js there is following code:
const ConnectorFormView = ({ AppValues, secureFetch, ...rest }) => {
return (
<p>Hello world</p>
)
}
export default ConnectorFormView;
Error is ocuring at the position of <p>.
I call this functional component from App A (frontend/src/views/Connector/ConnectorNewEditView/index.js) like this
import ConnectorFormView from "connector_frontend/src/views/Connector/FormView";
const ConnectorNewEditView = () => {
return (<ConnectorFormView AppValues={appValues} secureFetch={secureFetch} />)
}
export default ConnectorNewEditView;
I tried to return just a plain text from the ConnectorFormView component like this:
const ConnectorFormView = ({ AppValues, secureFetch, ...rest }) => {
return (
'Hello world'
)
}
export default ConnectorFormView;
and it was compiled successfully, but once I return a JSX from the ConnectorFormView component the compiler get crashed.
Can anyone explain the source of this error please?
I successfully figured out the source of this problem and also I found a solution. I expected I can reuse however React component implemented in JSX. I expected, the only requirement is to install it, resp. download it in JSX form to my project using NPM. It's FALSE.
According to my research, I can reuse React components compiled from JSX to ES only. It is achievable using Babel. Another very important requiremen, it should not by installed as React application as I installed it. It must be installed as a NPM package with clearly defined set of exported components.
I found this paper https://levelup.gitconnected.com/publish-react-components-as-an-npm-package-7a671a2fb7f and I followed it step by step and finally I achieved desired result.
I can very briefly mention steps which I performed:
Implemented my custom component (mentioned above)
const ConnectorFormView = ({ AppValues, secureFetch, ...rest }) => {
return (
<p>Hello world</p>
)
}
export default ConnectorFormView;
Added the component to /src/index.js file in order to export it for host App
import ConnectorFormView from './components/ConnectorFormView';
export { ConnectorFormView };
Installed Babel and all needed presets
npm install --save-dev #babel/core #babel/cli #babel/preset-env
npm install -save #babel/polyfill
configured Babel - created file /babel.config.json with following content
{
"presets": [
[
"#babel/env",
{
"targets": {
"edge": "17",
"firefox": "60",
"chrome": "67",
"safari": "11.1"
},
"useBuiltIns": "usage",
"corejs": "3.6.5"
}
],
"#babel/preset-react"
]
}
Added new script to the package.json to compile React components to ES:
"build": "rm -rf dist && NODE_ENV=production babel src --out-dir dist --copy-files";
Executed npm run build to compile React components to ES. Compiled components are located in new dist folder.
Adjusted package.json that looks like this now
{
"name": "my_react_components",
"version": "0.1.0",
"private": true,
"description": "Some description",
"author": "Lukas",
"main": "dist/index.js",
"module": "dist/index.js",
"files": [ "dist", "README.md" ],
"dependencies": {
.
.
},
"scripts": {
"build": "rm -rf dist && NODE_ENV=production babel src--out-dir dist --copy-files",
.
.
.
},
"eslintConfig": {
"extends": "react-app"
},
"devDependencies": {
"#babel/cli": "^7.17.10",
"#babel/core": "^7.18.5",
"#babel/preset-env": "^7.18.2"
}
}
Pushed dist folder to the git.
Installed repository to the host project using NPM
npm install https://github.com/my_custom/react_component.git
Import and use the component from installed git repo
import ConnectorFormView from 'my_react_components';
function App() {
return (
<ConnectorFormView />
);
}
export default App;
Actually that's all. I very recommend to see the attached paper at the beginning of this answer because I maybe skipped very important info for you.
Related
I'm trying to create custom react components based on Liferay's Clay components.
Using e.g. just a ClayButton works, but as soon as i try to use hooks (like React.useState), the browser console tells me:
Minified React error #321; visit https://reactjs.org/docs/error-decoder.html?invariant=321 for the full message
The full message tells me i could be using mismatching versions of react and react-dom. I'm not.
I also don't have 2 different versions of react, according to the test described there.
I created a minimal example module at https://github.com/ReFl3x0r/liferay-react-component-test which can be tested in a Liferay Gradle Workspace.
There's also an older thread in Liferay Forums discussing this error, but with no solution.
(https://liferay.dev/ask/questions/development/re-lr-7-3-react-portlet-invalid-hook-call)
What am i doing wrong?
EDIT:
Trying to point out the main code snippets.
First CustomButtonFail.es.js:
import React from 'react';
import ClayButton from '#clayui/button';
const CustomButton = () => {
const [name, setName] = React.useState('test');
return (
<ClayButton displayStyle='primary'>
TEST
</ClayButton>
);
}
export default CustomButton;
The package.json:
{
"dependencies": {
"#clayui/button": "^3.40.0",
"#clayui/css": "3.x",
"react": "^16.12.0",
"react-dom": "^16.12.0"
},
"devDependencies": {
"#liferay/npm-scripts": "47.0.0",
"react-test-renderer": "^16.12.0"
},
"name": "component-test",
"scripts": {
"build": "liferay-npm-scripts build"
},
"version": "1.0.0"
}
The view.jsp including the component (shortened):
<%#taglib uri="http://liferay.com/tld/react" prefix="react" %>
<div class="react-component-failing">
<react:component
module="js/CustomButtonFail.es"
/>
</div>
I finally got it working. Reducing package.json like this:
{
"devDependencies": {
"#liferay/npm-scripts": "47.0.0"
},
"name": "component-test",
"scripts": {
"build": "liferay-npm-scripts build"
},
"version": "1.0.0"
}
and adding a ".npmbundlerrc" in modules root with content:
{
"config": {
"imports": {
"frontend-taglib-clay": {
"#clayui/button": ">=3.40.0",
"#clayui/css": ">=3.x"
},
"#liferay/frontend-js-react-web": {
"react": ">=16.12.0"
}
}
}
}
did the trick.
Working example is at https://github.com/ReFl3x0r/liferay-react-component-test/tree/working
I've got a frontend (next.js) which has tailwindcss installed (config, postcss, ...) and everything works.
I've made another package (ui) which has the following package.json
{
"name": "ui",
"version": "1.0.0",
"private": true,
"peerDependencies": {
"react": "^17.0.2",
"react-dom": "^17.0.2",
"autoprefixer": "^10.3.2",
"postcss": "^8.3.6",
"tailwindcss": "^2.2.7"
}
}
The problem is when is serve the ui locally everything works fine (the UI sees the styles of the component), but when deployed to vercel, the component has no styles in it.
The component (ui):
import React from 'react';
const Example = ({children}) => <button className='bg-blue-500 py-1 px-2'>{children}</button>
export default Example
And my next config (frontend)
const withTM = require('next-transpile-modules')(['bar'])
module.exports = withTM()
Is there a way of sharing the same tailwind.config.js ? Or anything to make it work.
Steps that I have made:
created the workspace
added frontend package (next, and then i installed tailwind with all the steps from their docs)
added the ui package (installed the peerDependencies, see above)
created the component
added the ui package as a dependency in the frontend, yarn install, and then imported the component
yarn dev, and the styles are applied locally.
deployed to vercel, the button has only the children , no styles
UPDATE:
The problem is caused by the purging process at build time.
Is there any way to specify in the tailwind config to purge also the ui package?
UPDATE2:
I've tried to add the package (i've renamed it to "#example/ui") to the purge in next.config.js
module.exports = {
purge: [
'./pages/**/*.{js,ts,jsx,tsx}',
'./components/**/*.{js,ts,jsx,tsx}',
'./node_modules/#example/ui/src/*.{js,ts,jsx,tsx}'
],
darkMode: false, // or 'media' or 'class'
theme: {
extend: {},
},
variants: {
extend: {},
},
plugins: [],
}
The code in the UI package is inside of the src, has only the index.tsx file. I mention, locally still works fine.
Solved it !
In the purge array , I needed to add the node modules from the root of the project, not of the frontend
I am trying to create a WordPress plugin with React. I am following this link. I have the following package.json file
{
"name": "test-plugin",
"version": "1.0.0",
"private": true,
"description": "Sample plugin for React",
"main": "index.js",
"devDependencies": {
"#wordpress/scripts": "^13.0.3"
},
"scripts": {
"build": "wp-scripts build",
"check-engines": "wp-scripts check-engines",
"check-licenses": "wp-scripts check-licenses",
"lint:css": "wp-scripts lint-style",
"lint:js": "wp-scripts lint-js",
"lint:pkg-json": "wp-scripts lint-pkg-json",
"start": "wp-scripts start",
"test:e2e": "wp-scripts test-e2e",
"test:unit": "wp-scripts test-unit-js"
},
"author": "thor",
"license": "ISC"
}
I am enqueueing the react script as follows
wp_enqueue_script( 'react-script', plugin_dir_url(__DIR__) . 'build/index.js', ['wp-element'], $this->version, false );
ISSUES
The build react js file is not generated. I have followed the above link and everything was installed correctly. But no index.js file is created in build folder
The script is not working if I enqueue src/index.js instead of build/index.js. The script is loaded but not working as expected. I have the div with id my_app in one of the pages in which the script is loaded.
Following is the react script file
const { render, useState } = wp.element;
const Votes = () => {
const [votes, setVotes] = useState(0);
const addVote = () => {
setVotes(votes + 1);
};
return (
<div>
<h2>{votes} Votes test</h2>
<p>
<button onClick={addVote}>Vote!</button>
</p>
</div>
);
};
render(<Votes />, document.getElementById('my_app'));
Had the same problem with you and I spent good couple of hours trying to fix this. Here are problem that I encountered:
One of the issues was the node_modules folder was in the wrong place, supposed to be at the same level as your theme.
The other one was I had to run npm run start instead of npm start. Whenever wp-scripts output something like this :
asset index.js 4.35 KiB [emitted] (name: index)
1 related asset asset index.asset.php 107 bytes [emitted] (name: index)
means it is working. If don't get that means something wrong with your folder location.
Try and let me know.
We can start with React project using command create-react-app. But when we use this command we don't get idea about how it's bundle and pack all types of files using Webpack.
So I tried to build react app without create-react-app command using Webpack 4 and Babel 7 and I succeed.
Now I want to do same app using parcel bundler. as I read on different blogs I got an idea that parcel is easy then web pack and it's truly zero configuration set up.
My project structure is
- src
|- components
|- styles
|- index.html
|- index.js
- package.json
This is simple project to start with parcel bundle so I've not add Redux or routing yet.
What I want to achieve is
1). As aspected React app should be run in browser using parcel bundler.
2). I want to use absolute paths when importing JS files.
3). I want to enable hot reloading.
After few minutes of research on net I find the solution and it's truly zero configuration with parcel bundler.
we just need to add 2 commands in package.json file 's script section.
project structure is
- src
|- components
|- styles
|- index.html
|- index.js
|- App.js
- package.json
- .babelrc // this file will use to setup absolute path when import files
index.js
import React from "react";
import ReactDOM from "react-dom";
import App from './App';
import "./style/index.scss";
ReactDOM.render(<App />, root);
index.html is same file as create-react-app provide we just need div with id.
App.js
import React, { Component } from "react";
import Count from "#components/Count"; // here I used absolute path. check .bablerc file
export default class App extends Component {
render() {
return (
<>
<div>this is App component</div>
<Count />
</>
);
}
}
components/Count.js
import React, { Component } from "react";
export default class Test extends Component {
constructor(props) {
super(props);
this.state = {
count: 0
};
}
render() {
return (
<>
count is: {this.state.count}
<br />
<button onClick={(prevState) => {this.setState({
count: prevState.count + 1
});
}}
>
Add count
</button>
</>
);
}
}
style/index.scss
body {
background-color: red;
color: #fff;
text-align: center;
font-size: 40px;
}
note that we don't need to manually install node-sass package to use
scss. parcel will handle for you.
package.json
{
"name": "react_parcel",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "parcel ./src/index.html",
"build": "parcel build ./src/index.html/"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"parcel-bundler": "^1.12.3",
},
"dependencies": {
"react": "^16.8.6",
"react-dom": "^16.8.6"
}
}
.bablerc
{
"plugins": [
[
"module-resolver",
{
"root": ["./src"],
"alias": {
"#components": "./src/components"
}
}
],
["react-hot-loader/babel"]
]
}
here we add alias for components folder so when we need to import any file from components folder we don't need to write relative path.
npm run dev
your server will start on localhost:1234
I solved 2 issues but still need to figure out how to use hot reloading so if you guys have any idea related hot reloading please answer it.
for production build: npm run build
I have a NextJS website I'm building and it's great, except that it does not work in IE 10/11 because some code is not being transpiled correctly. I'm really bad with babel and webpack, and have never had to config them myself before. I've been trying to solve by reading online for two days now, and nothing seems to work for me.
The exact error I get is weakSet is not defined, and it is coming from the common.js file.
Here is my .babelrc file, located in the root of my project.
// .babelrc
{
"presets": ["next/babel"],
"plugins": [
["styled-components", {
"ssr": true,
"displayName": true,
"preprocess": false
}]
]
}
my package.json
{
"name": "bradshouse",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"dev": "next",
"build": "next build",
"start": "next start"
},
"keywords": [],
"author": "",
"license": "ISC",
"dependencies": {
"next": "^7.0.2",
"react": "^16.6.0",
"react-dom": "^16.6.0",
"react-pose": "^4.0.1",
"styled-components": "^4.0.2"
},
"devDependencies": {
"babel-plugin-styled-components": "^1.8.0"
}
}
The full repo is here if you're interested in launching it yourself. node modules are excluded, so npm install, then npm run build, then npm run start.
https://github.com/TJBlackman/Brads-House
Thanks for the help! Feel free to just link articles and I'll read them thoroughly! Thanks.
Edit: As a quick fix, I added this polyfill script to the <head> of all my pages, and it still did not fix this issue... Wat?! <script src="https://cdn.polyfill.io/v2/polyfill.min.js"></script>
How to support IE 11 by transpiling node_modules code in NextJS
Original answer found in the ziet/nextjs issues thread (view here). Shout out to joaovieira <3
npm install --save-dev babel-polyfill
create a polyfill.js where ever you want it, and inside that file, import the babel-polyfill like so:
import 'babel-polyfill';
Next, if you do not have a next.config.js file, create it at your root directory. Now update your this file to include the following webpack config. Notice how it's using the polyfill file you just made. Full file example:
module.exports = {
webpack: (config) => {
// Unshift polyfills in main entrypoint.
const originalEntry = config.entry;
config.entry = async () => {
const entries = await originalEntry();
if (entries['main.js']) {
entries['main.js'].unshift('./path_to/polyfills.js'); // <- polyfill here
}
return entries;
};
return config;
}
}
Finally, if you don't have a .babelrc file in your project's root directory, create one. Inside it, use the code below that matches the version of babel you're using.
Babel 7
{
"presets": [
[
"next/babel",
{
"preset-env": {
"useBuiltIns": "usage"
}
}
]
]
}
Babel 6
{
"presets": [
[
"next/babel",
{
"preset-env": {
"targets": {
"browsers": "defaults"
},
"useBuiltIns": true
}
}
]
]
}
That's all I know. I'm not even thinking about IE 10...
Good luck!!
I am using next#9.3 with reflect-metadata, and here is my solution:
/** next.config.js > webpack **/
const pathToPolyfills = './src/polyfills.js'
// Fix [TypeError: Reflect.metadata is not a function] during production build
// Thanks to https://leerob.io/blog/things-ive-learned-building-nextjs-apps#polyfills
// There is an official example as well: https://git.io/JfqUF
const originalEntry = config.entry
config.entry = async () => {
const entries = await originalEntry()
Object.keys(entries).forEach(pageBundleEntryJs => {
let sourceFilesIncluded = entries[pageBundleEntryJs]
if (!Array.isArray(sourceFilesIncluded)) {
sourceFilesIncluded = entries[pageBundleEntryJs] = [sourceFilesIncluded]
}
if (!sourceFilesIncluded.some(file => file.includes('polyfills'))) {
sourceFilesIncluded.unshift(pathToPolyfills)
}
})
return entries
}
/** src/polyfills.js **/
// Other polyfills ...
import 'reflect-metadata'