How To Create Worker in React - reactjs

I'm trying to create a worker in an app created from create-react-app using react 16.8.6 and yarn 1.16.0. If I use
const backgroundWorker = new Worker('../assets/js/myWorker.js');
I get the console error: Uncaught SyntaxError: Unexpected token <
But I know that is the correct path. This works fine in Angular. Is there a good tutorial on how to create a worker in React?

The directory "assets" is the public directory for #angular/cli projects, not create-react-app projects. In create-react-app the equivalent of #angular/cli "assets" is "public" which is described in the create-react-app documentation under Using the Public Folder. Move your "js" directory and the "myWorker.js" file to the public directory and update the creation of the worker to point to that path instead:
const backgroundWorker = new Worker('/js/myWorker.js');
You can also use process.env.PUBLIC_URL instead:
const backgroundWorker = new Worker(`${process.env.PUBLIC_URL}/js/myWorker.js`);
Hopefully that helps!

Follow these steps in order to add your worker files to your create-react-app project.
1. Install these three packages:
$ yarn add worker-plugin --dev
$ yarn add comlink
$ yarn add react-app-rewired --dev
2. Override webpack config:
In your project's root directory create a config-overrides.js file with the following content:
const WorkerPlugin = require("worker-plugin");
module.exports = function override(config, env) {
//do stuff with the webpack config...
config.plugins = [new WorkerPlugin({ globalObject: "this"}), ...config.plugins]
return config;
}
3. Replace your npm scripts inside package.json by:
"start": "react-app-rewired start",
"build": "react-app-rewired build",
4. Create two files in order to test your configuration:
worker.js
import * as Comlink from "comlink";
class WorkerWorld {
sayHello() {
console.log("Hello! I am doing a heavy task.")
let numbers = Array(500000).fill(5).map(num => num * 5);
return numbers;
}
}
Comlink.expose(WorkerWorld)
use-worker.js
import * as Comlink from "comlink";
const initWorker = async () => {
const workerFile = new Worker("./worker", { name: "my-worker", type: "module" });
const WorkerClass = Comlink.wrap(workerFile)
const instance = await new WorkerClass();
const result = await instance.sayHello();
console.log("Result of my worker's computation: ", result);
}
initWorker()
5. See the output:
$ yarn start

Related

How to configure the start script in webpack to use the api I want?

How to configure the start script in webpack to use the api I want?
For example when I run "npm start" my webpack source will use the productuion api like 'https://abc_loginapi.com, and when I run "npm run dev" the webpack source Mine will use local apis like http://localhost:9500/login.
My current way of doing it is quite manual, when I want to run one, I will comment the other one
export const API_HOST_LIST =
{
HostBaseURL: 'https://abc_loginapi.com'
// HostBaseURL: 'http://localhost:9500/login'
}
Is there any way to handle this problem, my source is webpack4 + reactJS
You can use environment variables to handle it, add env to script with --env argument, like this:
webpack --env prod // result { prod: true }
And call it in webpack config file
const path = require('path');
module.exports = env => {
// Use env.<YOUR VARIABLE> here:
console.log('Production: ', env.prod); // true
return {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist'),
},
};
};
For more detail, check Webpack docs here
Like Phr0gggg mentions, using environment variables is one option.
Another option is to use the "NODE_ENV" variable.
In dev mode, process.env.NODE_ENV is equal to 'development' and in production, it's equal to 'productoin'
And you can use something like this:
const isProduction = process.env.NODE_ENV === "production"
const hostBaseUrl = () => {
if (isProduction) {
return 'http://localhost:9500';
}
return 'https://abc_loginapi.com';
}
export const API_HOST_LIST =
{
HostBaseURL: hostBaseUrl()
}
You can use process.env api to judge in webpack,
The code in webpack file like.
export const API_HOST_LIST =
{
HostBaseURL: process.env.NODE_ENV ==='dev'? 'http://localhost:9500/login':'https://abc_loginapi.com'
}
The code in package.json file like.
"scripts": {
"dev": "NODE_ENV=dev npm run start",
"start": "npm start"
}

My own create-react-app template(react-boilerplate) cannot be installed by npx command with "chalk" issue(node:internal/errors:465)

I've made a cra-template which could be installed by npx command.
Npx command goes to <rootDir>/bin/cli.js and it imports chalk#4.1.2
On local environment it works very well(node bin/cli.js <my-app-name>)
But whenever I try to install by npx create-... <my-app-name> it always alert error below
Need to install the following packages:
create-...#latest
Ok to proceed? (y) y
------------------------------------------------------------------------
node:internal/errors:465
ErrorCaptureStackTrace(err);
^
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'chalk' imported from
/<to .npm>/.npm/_npx/<my npx hash>/node_modules/<create-...>/bin/cli.mjs
❌ I've changed my cli.js file to cli.mjs cause of Chalk 5 has changed to ESM
❌ I downgraded chalk#5 to chalk#4.1.2. It doesn't work at all with same issue
❌ I also changed cli.mjs to cli.js which allows const..require.. with chalk#4.1.2
ONLY WHEN I ACCESS TO /<to .npm>/.npm/_npx/<my npx hash> and npm i chalk#4.1.2 IT WORKS
I'm facing this issue about 3days. I tried to find out "How npx command works", "Chalk >issues", "Error code:465" etc...
This issue is way over my head. Please be my life saver.......
Here's my code
// <rootDir>/bin/cli.js
#!/usr/bin/env node
import { execSync } from "child_process";
import { fileURLToPath } from "url";
import chalk from "chalk";
import path from "path";
const runCommand = (command) => {
try {
execSync(`${command}`, { stdio: "inherit" });
return true;
} catch (err) {
console.log(`Failed to run ${command}`);
console.log(err);
return false;
}
};
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const dirName = process.argv[2];
const gitCheckoutCommand = `git clone --depth 1 <my git repo>;
const npmInstallCommand = `cd ${dirName} && npm install`;
// Create new create-react-app-lite
console.log(
chalk.blueBright(`Creating a new React app in`),
chalk.magentaBright.bold.italic.underline(
`${path.resolve(process.cwd(), `../../../../${dirName}`)}`,
),
);
const checkedOut = runCommand(gitCheckoutCommand);
if (!checkedOut) {
process.exit(1);
}
// Install dependencies
... console.log() command with chalk
// Inform success and commands to start, build, test, prettier, and lint
... console.log() command with chalk

When does webpack cli comes into play

How is webpack-cli used in a project?
From what I understand, as soon as I enter npm run start on my bash terminal, webpack starts running the webpack config file where I have written rules to convert jsx to js using babel, scss/less to css (correct me if I'm wrong).
But where does webpack-cli comes into play in all this?
The webpack-dev-server package is responsible to serve the build over an http server that it creates for it. It also re-starts if you make any changes to the source code (when using the hot reload option).
On the other hand, the webpack-cli package is responsible for the build and bundle of the source files. So, webpack-dev-server has to run the webpack-cli.
So you've got to have both of the packages installed.
You can see kind of how it does that in here:
https://github.com/webpack/webpack-dev-server/blob/master/bin/webpack-dev-server.js
/** #type {CliOption} */
const cli = {
name: 'webpack-cli',
package: 'webpack-cli',
binName: 'webpack-cli',
installed: isInstalled('webpack-cli'),
url: 'https://github.com/webpack/webpack-cli',
preprocess() {
process.argv.splice(2, 0, 'serve');
},
};
// ...
const runCli = (cli) => {
if (cli.preprocess) {
cli.preprocess();
}
const path = require('path');
const pkgPath = require.resolve(`${cli.package}/package.json`);
// eslint-disable-next-line import/no-dynamic-require
const pkg = require(pkgPath);
// eslint-disable-next-line import/no-dynamic-require
require(path.resolve(path.dirname(pkgPath), pkg.bin[cli.binName]));
};
// ...
runCommand(packageManager, installOptions.concat(cli.package))
.then(() => {
runCli(cli);
})
.catch((error) => {
console.error(error);
process.exitCode = 1;
});
In webpack v5, that order kind of got reversed since you use webpack server, which is a webpack-cli call, to initiate the serve, that will call the webpack-dev-server package.
I'm not a webpack expert by any means, but I think this will help you to understand it better.

Unable to resolve an image which is shared between web (react) and mobile (react-native) projects

I have a custom npm package created in directory web_with_weback (web react project). The package is built using webpack and contains one .png image. The package is loaded by using npm link inside another directory which is a react native project named mobile_with_metro. So in short, I want to have an npm package which among other things will share images from web to mobile. So to give you some context, here is how the two projects are setup.
The web project has the following structure.
Where the index.ts contains:
import TestImage from './testimage.png';
export {
TestImage
};
webpack.config.js
const { CleanWebpackPlugin } = require('clean-webpack-plugin');
const nodeExternals = require('webpack-node-externals');
const CopyPlugin = require("copy-webpack-plugin");
module.exports = {
entry: {
images: {import: './src/images/index.ts', filename: 'images/index.js'}
},
externals: [nodeExternals()],
output: {
libraryTarget: 'commonjs'
},
plugins: [
new CleanWebpackPlugin(),
new CopyPlugin({
patterns: [
{ from: "src/images/index.d.ts", to: "images/index.d.ts" }
],
}),
],
module: {
rules: [
{
test: /\.(ts|tsx)$/,
exclude: /node_modules/,
use: ['babel-loader']
},
{
test: /\.(png|jpe?g|gif|svg)$/i,
type: 'asset/inline'
}
]
}
}
I use webpack (v5.19.0). I use the latest feature from webapack asset/inline which is a replacement for the previous used url-loader.
dist folder after webpack build looks the following.
By using npm link in the dist folder and npm link #test/web_with_webpack in mobile_with_metro I make the built package available in the mobile app directory.
Now when I try to import the image and render it in an Image component in react native I get the following error:
error: Error: Unable to resolve module `#test/web_with_webpack/images` from `App.tsx`: #test/web_with_webpack/images could not be found within the project or in these directories:
node_modules/#test/web_with_webpack
If you are sure the module exists, try these steps:
1. Clear watchman watches: watchman watch-del-all
2. Delete node_modules: rm -rf node_modules and run yarn install
3. Reset Metro's cache: yarn start --reset-cache
4. Remove the cache: rm -rf /tmp/metro-*
at ModuleResolver.resolveDependency (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/node-haste/DependencyGraph/ModuleResolution.js:186:15)
at ResolutionRequest.resolveDependency (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/node-haste/DependencyGraph/ResolutionRequest.js:52:18)
at DependencyGraph.resolveDependency (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/node-haste/DependencyGraph.js:287:16)
at Object.resolve (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/lib/transformHelpers.js:267:42)
at /home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/DeltaBundler/traverseDependencies.js:434:31
at Array.map (<anonymous>)
at resolveDependencies (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/DeltaBundler/traverseDependencies.js:431:18)
at /home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/DeltaBundler/traverseDependencies.js:275:33
at Generator.next (<anonymous>)
at asyncGeneratorStep (/home/dpordan/dev/test_webpack_metro/mobile_with_metro/node_modules/metro/src/DeltaBundler/traverseDependencies.js:87:24)
Here is the react native App.tsx which imports the image.
import React from 'react';
import {
Image
} from 'react-native';
import {TestImage} from '#test/web_with_webpack/images';
const App = () => {
return (
<>
<Image source={{
uri: TestImage
}}/>
</>
);
};
To me it seems that the metro bundler can't understand what webpack has generated as an image 'path'.
Is there anything that I am missing here?
EDIT:
I tried using it in another react web project (which is setup with CRA) and it works. So, there is something that metro bundler in react native does not understand in the built package.

When specified, "proxy" in package.json must be a string

I would like to have proxy in my react client, my package.json contains:
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"proxy": {
"/auth/google": {
"target": "http://localhost:5000"
}
},
...
But when I ran it, I got error
When specified, "proxy" in package.json must be a string.
[1] Instead, the type of "proxy" was "object".
[1] Either remove "proxy" from package.json, or make it a string.
I tried to convert to string, no errors but proxy is not working
"proxy": "http://localhost:5000"
My App.js
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<p>hey there</p>
Sign In With Google
</header>
</div>
The issue that you are facing is because of CRA v2.
Firstly, you will not require any additional configuration if you are just using a plain string in your proxy. But the moment you use an object, you are using advanced configuration.
So, you would have to follow the steps listed below:
Install http-proxy-middleware by typing npm i --save http-proxy-middleware
Remove the entries from package.json:
"proxy": {
"/auth/google": {
"target": "http://localhost:5000"
}
}
Now create a setup file for your proxy. You should name it setupProxy.js in your src folder on the client side and type the following code:
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(proxy('/auth/google',
{ target: 'http://localhost:5000/' }
));
}
for more info check this
I think it is "create-react-app" issue.
You can go to https://github.com/facebook/create-react-app/issues/5103
to migration to the new proxy handling method.
For short, you just need to install a new library called "http-proxy-middleware"
npm install http-proxy-middleware --save
And then create a new file "src/setupProxy.js", and type
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(proxy('/auth/google', { target: 'http://localhost:5000/' }));
};
Hope this can solve your problem, happy hacking!
First, install http-proxy-middleware using npm or Yarn:
$ npm install http-proxy-middleware --save
$ # or
$ yarn add http-proxy-middleware
Next, create src/setupProxy.js and place the following contents in it:
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
// ...
}
Now, migrate each entry in your proxy object one by one, e.g.:
"proxy": {
"/api": {
"target": "http://localhost:5000/"
},
"/*.svg": {
"target": "http://localhost:5000/"
}
}
Place entries into src/setupProxy.js like so:
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(proxy('/api', { target: 'http://localhost:5000/' }))
app.use(proxy('/*.svg', { target: 'http://localhost:5000/' }))
}
You can also use completely custom logic there now!
I have got this working response from this link and hence sharing-https://github.com/facebook/create-react-app/issues/5103
For people in 2020,
Install http-proxy-middleware by typing npm i --save http-proxy-middleware inside the client folder.
Remove the entries from package.json:
"proxy": {
"/auth/google": {
"target": "http://localhost:5000"
}
}
Now create a setup file for your proxy. You should name it setupProxy.js in your src folder on the client side and type the following code:
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/auth/google", { target: "http://localhost:5000/" })
);
};
PS: You don't need to include setupProxy.js anywhere in server.js or index.js. just copy and paste.
The following worked for me:
Remove "proxy" from your package.json.
Install 'http-proxy-middleware' in the client directory. To do this, cd into the client directory and run "npm i --save http-proxy-middleware". Then, create a new file in the src directory of your client called "setupProxy.js". Place the following code in this file:
const { createProxyMiddleware } = require('http-proxy-middleware');
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(createProxyMiddleware('/api/', // replace with your endpoint
{ target: 'http://localhost:8000' } // replace with your target
));
}
restart the server, and you should be good to go.
Change the proxy to something like this and hope it will work as it worked for me.
"proxy": "http://localhost:5000/auth/google"
At this moment i'm using React 16.8.13 this works fine:
1- delete "proxy": {***} from package.json file
2- type npm install http-proxy-middleware
3- create the file src/setupProxy.js
4-insert the code as following:
const {createProxyMiddleware} = require('http-proxy-middleware');
module.exports = (app) => {
app.use(
createProxyMiddleware('/endpoint/*', {
target: 'http://address/',
secure: false,
}),
);
};
If you need to proxy requests and rewrite urls, for example localhost:3000/api/backend/some/method to https://api-server.example.com/some/method, you need to use pathRewrite option also:
const {createProxyMiddleware} = require("http-proxy-middleware");
module.exports = function(app) {
app.use(
"/api/backend",
createProxyMiddleware({
target: "https://api-server.example.com",
changeOrigin: true,
pathRewrite: {
"^/api/backend": "",
},
})
);
};
install "http-proxy-middleware" into your client, "not inside server".
Add setupProxy.js inside of your client/src/ directory.
(should be like this: client/src/setupProxy.js)
Add the below lines to it.
const proxy = require("http-proxy-middleware");
module.exports = app => {
app.use(proxy("/auth/google", { target: "http://localhost:5000/" }));
};
That's it, get inside of your google dev console and add localhost:3000/auth/google/callback to your project.
https://github.com/facebook/create-react-app/issues/5103
Move advanced proxy configuration to src/setupProxy.js
This change is only required for individuals who used the advanced proxy configuration in v1.
To check if action is required, look for the proxy key in package.json. Then, follow the table below.
I couldn't find a proxy key in package.json
No action is required!
The value of proxy is a string (e.g. http://localhost:5000)
No action is required!
The value of proxy is an object
Follow the migration instructions below.
If your proxy is an object, that means you are using the advanced proxy configuration.
Again, if your proxy field is a string, e.g. http://localhost:5000, you do not need to do anything. This feature is still supported and has the same behavior.
First, install http-proxy-middleware using npm or Yarn:
$ npm install http-proxy-middleware --save
$ # or
$ yarn add http-proxy-middleware
Next, create src/setupProxy.js and place the following contents in it:
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
// ...
}
Now, migrate each entry in your proxy object one by one, e.g.:
"proxy": {
"/api": {
"target": "http://localhost:5000/"
},
"/*.svg": {
"target": "http://localhost:5000/"
}
}
Place entries into src/setupProxy.js like so:
const proxy = require('http-proxy-middleware')
module.exports = function(app) {
app.use(proxy('/api', { target: 'http://localhost:5000/' }))
app.use(proxy('/*.svg', { target: 'http://localhost:5000/' }))
}
You can also use completely custom logic there now! This wasn't possible before.
It's worked.
app.use(
'/api',
proxy({ target: 'http://www.example.org', changeOrigin: true })
);
changeOrigin:true
In my cases i didn't need src/setupProxy.js...
I do that with axios... Check About Axios Proxy
Check in node library if you have it or not: http-proxy-middleware is optional i didn't need it!!!
Just try to restart server side, and that's it!!!
Add to check:
componentDidMount(){
axios.get('/api/path-you-want').then(response=>{
console.log(response)
})
}
This is related to a bug in create-react-app version2.
Just run
$ npm install react-scripts#next --save
$ # or
$ yarn add react-scripts#next
Answer found at:
https://github.com/facebook/create-react-app/issues/5103
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"proxy": {
"/auth/google": {
"target": "http://localhost:5000"
}
},
...
When specified, "proxy" in package.json must be a string.
Just change `"proxy": "http://localhost:5000"` and you are good to go.
If that doesn't solve the problem then register your proxy using **http-proxy-middleware**
$ npm install http-proxy-middleware --save
$ # or
$ yarn add http-proxy-middleware
Then create setypProxy.js file under src directory the put the following code.
const proxy = require('http-proxy-middleware');
module.exports = app => {
app.use(
proxy('/auth/google', {
target: 'http://localhost:5000'
})
);
app.use(
proxy('/auth/facebook', {
target: 'http://localhost:6000'
})
);
};
Create a setupProxy.js file inside the src folder and copy-paste the below code.
const { createProxyMiddleware } = require("http-proxy-middleware");
module.exports = function (app) {
app.use(
createProxyMiddleware("/auth/google", {
target: "http://localhost:5000/",
})
);
};
This worked for me (just as several people have already replied). But I write this just in case someone asks whether this is still a valid answer in 2021.
Delete this from your package.json file:
"proxy": {
"/auth/google": {
"target": "http://localhost:5000"
}
Install proxy middleware by running npm install --save http-proxy-middleware.
Create setupProxy.js file in your src (right next to the index.js file) file on the frontend.
In that setupProxy.js file put:
const proxy = require('http-proxy-middleware');
module.exports = function(app) {
app.use(proxy('/auth/google',
{ target: 'http://localhost:5000/' }
));
Of course, your port can be anything. It does not have to be 5000. Where ever you are running your backend service at.
That is it. You do not have to import this file anywhere. It works as it is.
After creating a file in the client side (React app ) called
src/setupProxy.js make sure you restart the server. The package.json file needs to restarted since you were dealing with a file outside the source directory.

Resources