In my React App, I want to implement push notification.
What I've done so far.
In the service worker registration step, I added some codes to initialize push manager that handles user subscription, now my app will prompt user to subscribe to push notification.
After subscription, from what I read, the final step is to register event listeners that catches pushes and listen to push notification click. Something like : self.addEventListener('push', function() { // Some codes here })
Now, where should I put the listener codes ? As CRA auto-generated the service-worker for production build
This doco suggests adding it after fetch in the registerServiceWorker.js.
The following solution works only CRA 1.x.x!
As I understood CRA doesn't support extending its service-worker.js from the box.
I've found the following workaround.
1) Install the cra-append-sw
npm install cra-append-sw --save
2) Add an extension file custom-sw.js to the root directory
self.addEventListener("push", event => {
const data = event.data.json()
const { title } = data
const body = {
body: data.body,
icon: data.icon
}
event.waitUntil(self.registration.showNotification(title, body))
})
3) Patch the package.json to run the cra-append-sw during debugging and building
"scripts": {
...
"start": "npm-run-all -p watch-css start-js && cra-append-sw --mode dev ./custom-sw.js",
...
"build": "npm-run-all build-css build-js && cra-append-sw ./custom-sw.js",
}
For anyone looking for this, here is a good guide on how to achieve push notifications in React.
Guide: Integrating Push Notifications in your Node/React Web App
Related
I understood that Ionic cannot handle 'yarn workspace' which build the project with react-app-rewired https://github.com/ionic-team/ionic-cli/issues/4430
As suggested in this post, I configure my monorepo with the multi-app option( https://ionicframework.com/docs/cli/configuration#multi-app-projects)
To valide the project structure, I firstly tried to share a simple constants.
import * as ROLES from '../../../../lib/constants'
but when I launch ionic serve --project=app
Have got this error :
Module not found: You attempted to import ../../../../lib/constants
which falls outside of the project src/ directory. Relative imports
outside of src/ are not supported.
Project structure :
ionic.config.json
{
"defaultProject": "myProject",
"projects": {
"app": {
"name": "My First Ionic App",
"type": "react",
"root": "packages/app",
"integrations": {
"capacitor": {}
},
}
}
}
How to manage to share files and components ?
Thank you
Ok, after several day. I have an answer to build monorepo with Ionic & Capacitor !
1 - Build your monorepo with Yarn workspace
this tutorial is simple and clear : https://jibin.tech/monorepo-with-create-react-app/
Then you will use react-app-rewired and not 'react-scripts` anymore
2 - Then do not use ionic serve that use react-scripts
react-app-rewired start
3 - and Capacitor !
The command ionic cap run also use react-scripts then you have to find an alternative
The answer is here https://capacitorjs.com/docs/guides/live-reload : Do not use the Ionic CLI
Capacitor supports CLIs with live reload capability.
Within capacitor.config.json, create a server entry then configure the url field
using the local web server’s IP address and port:
"server": { "url": "http://192.168.1.68:8100", "cleartext": true
},
Bonus : I created a script to automatically update capacitor.config.json
https://gist.github.com/damienromito/e6b3930ffde4c7240f109d1de69febb5
Is it possible to set environment variables in the manifest.json file of a Chrome Extension?
Like wOxxOm said, I used webpack to proccess manifest.json.
In my case, I needed to set version automatically on manifest file.
I added to webpack script:
plugins: [
new CopyWebpackPlugin([
{
from: "public/manifest.json",
to: "manifest.json",
transform(content, path) {
return modify(content)
}
}
]),
]
And the modify function replaces version on file for the parameter:
function modify(buffer) {
var manifest = JSON.parse(buffer.toString());
let argv = process.argv[2];
if (argv) manifest.version = argv.split("=")[1];
let manifest_JSON = JSON.stringify(manifest, null, 2);
return manifest_JSON;
}
So, I build like "yarn build --version=x.x" and webpack do what I need.
PS: if you're going to use this, remember to change:
the manifest.json directory, if necessary;
the value in the modify function, in my case it was version
As the OP has mentioned in her answer, using the copy-webpack-plugin in the webpack.config.js file is the way to go if you're building your Chrome Extension with React. However, if your React app is based on create-react-app, directly editing the webpack.config.js file (which is located in node_modules/react-scripts/config) is not recommended.
In such a case, use craco, which is an npm package that can be used to customize an app based on create-react-app. Here's how you do it:
Install craco into your project using npm i #craco/craco.
Install copy-webpack-plugin as a dev-dependency in your project using npm i --save-dev copy-webpack-plugin.
Let's suppose we're creating a development and a production build of our Chrome Extension. Let's also suppose we've already assigned "version": "0.1.0" in our Chrome Extension's manifest.json. Depending on the build type, we'd like to assign accordingly the version_name field in our Chrome Extension's manifest.json, e.g., "version_name": "0.1.0 dev" for development and "version_name": "0.1.0" for production. In your React app's package.json, introduce two fields (the script names can be whatever you wish) as follows:
"scripts": {
...
"build-dev": "CRX_ENV=dev craco build", // or "set CRX_ENV=dev&& craco build" in the case of Windows
"build-prod": "CRX_ENV=prod craco build", // or "set CRX_ENV=prod&& craco build" in the case of Windows
...
}
Create a new file called craco.config.js in the root of your project. As per your need, do something similar to the following in the craco.config.js file:
const CopyPlugin = require("copy-webpack-plugin")
module.exports = {
webpack: {
plugins: [
new CopyPlugin({
patterns: [
{
from: "public/manifest.json",
to: "manifest.json",
transform(content, path) {
return modifyManifest(content)
},
},
],
}),
],
},
}
function modifyManifest(buffer) {
const manifest = JSON.parse(buffer.toString())
if (process.env.CRX_ENV === "dev") {
manifest.version_name = `${manifest.version} dev`
} else if (process.env.CRX_ENV === "prod") {
manifest.version_name = `${manifest.version}`
}
const manifestJson = JSON.stringify(manifest, null, 2)
return manifestJson
}
Run npm run build-dev. It will create a folder called build in your project root. This build folder is your unpacked Chrome Extension, which you can load into Chrome using the "Load unpacked" button on the chrome://extensions page. Once loaded, you should be able to see 0.1.0 dev as the version name of your Chrome Extension.
Delete the build folder created from the previous step and run npm run build-prod, and repeat the same steps. You should be able to see 0.1.0 as the version name of your Chrome Extension on the chrome://extensions page.
I have a question - how to analyze bundle size?
I want to get informations how bundle files change in case i will push the commit in gitlab.
I was looking for something like danger.js but it probably doesn't support gitlab.
You can use this script to analyze, without ejecting create-react-app
Put analyze.js in root of your project ( where the package.json is located )
npm install progress-bar-webpack-plugin
npm install webpack-bundle-analyzer
analyze.js
process.env.NODE_ENV = 'production';
const webpack = require('webpack');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const webpackConfigProd = require('react-scripts/config/webpack.config')('production');
// this one is optional, just for better feedback on build
const chalk = require('chalk');
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
const green = text => {
return chalk.green.bold(text);
};
// pushing BundleAnalyzerPlugin to plugins array
webpackConfigProd.plugins.push(new BundleAnalyzerPlugin());
// optional - pushing progress-bar plugin for better feedback;
// it can and will work without progress-bar,
// but during build time you will not see any messages for 10-60 seconds (depends on the size of the project)
// and decide that compilation is kind of hang up on you; progress bar shows nice progression of webpack compilation
webpackConfigProd.plugins.push(
new ProgressBarPlugin({
format: `${green('analyzing...')} ${green('[:bar]')}${green('[:percent]')}${green('[:elapsed seconds]')} - :msg`,
}),
);
// actually running compilation and waiting for plugin to start explorer
webpack(webpackConfigProd, (err, stats) => {
if (err || stats.hasErrors()) {
console.error(err);
}
});
Now simply put node ./analyze.js in the package.json scripts
"scripts": {
.....
"analyze": "node ./analyze.js"
},
after that run npm run analyze
You can also accomplish this using webpack stats json file, supported by create-react-app and webpack-bundle-analyzer.
When running your build with create-react-app, add the --stats flag:
yarn build --stats
This will create a build/bundle-stats.json file, with webpack stats, allowing you to use the webpack-bundle-analyzer CLI to analyze this stats.
yarn run webpack-bundle-analyzer build/bundle-stats.json
reference:
https://www.npmjs.com/package/webpack-bundle-analyzer#user-content-usage-as-a-cli-utility
tested with CRA latest version at the time: v4
You can use and configure webpack-bundle-analyzer library and use it in your React App WITHOUT EJECTING
Add some dependencies by executing npm install --save-dev progress-bar-webpack-plugin webpack-bundle-analyzer
Create a new folder named scripts at the root of your React App.
Create a new file analyze_build_bundle.js in the scripts folder and add the below code in that file
// script to enable webpack-bundle-analyzer
process.env.NODE_ENV = 'production';
const webpack = require('webpack');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const webpackConfigProd = require('react-scripts/config/webpack.config')(
'production'
);
const ProgressBarPlugin = require('progress-bar-webpack-plugin');
webpackConfigProd.plugins.push(new BundleAnalyzerPlugin());
webpackConfigProd.plugins.push(
new ProgressBarPlugin({
format: `analyzing... [:bar] [:percent] [:elapsed seconds] - :msg`,
})
);
// actually running compilation and waiting for plugin to start explorer
webpack(webpackConfigProd, (err, stats) => {
if (err || stats.hasErrors()) {
console.error(err);
}
});
Add the analyze-build-bundle command in your package.json file to run the webpack-bundle-analyzer as below:
"scripts": {
"analyze-build-bundle": "node scripts/analyze_build_bundle.js",
"start": "react-scripts start",
.....
...
}
Now execute command npm run analyze-build-bundle. This will build your app and then start the analyzer local server and you should be able to see the screen as shown in below image
I'm trying to build a page using Create React App, plus the sqlite3 module. In their default configurations, the two things don't seem to be compatible out of the box. I'm new to React and JS in general, so I'm hoping there's something obvious I'm missing here.
Using npx v6.9.0 and Node v12.4.0, I can reproduce this by typing npx create-react-app test, cd test, npm start.
So far so good. I type: npm install sqlite3, and receive an npm warning that I should install typescript. OK, I type npm install typescript. All good. I start up the app, and it compiles so far. Great!
I open up App.js, and per sqlite3's readme doc, under the import lines, I type var sqlite3 = require('sqlite3').verbose();
This is probably where I'm doing something wrong. My app now fails to compile, telling me:
./node_modules/sqlite3/node_modules/node-pre-gyp/lib/info.js
Module not found: Can't resolve 'aws-sdk' in '/Users/brendanlandis/Desktop/test/node_modules/sqlite3/node_modules/node-pre-gyp/lib'
I try npm install aws-sdk, which gets me a little farther. My app still won't compile, but now the error is:
TypeError: stream is undefined
./node_modules/set-blocking/index.js/</module.exports/<
node_modules/set-blocking/index.js:3
1 | module.exports = function (blocking) {
2 | [process.stdout, process.stderr].forEach(function (stream) {
> 3 | if (stream._handle && stream.isTTY && typeof stream._handle.setBlocking === 'function') {
4 | stream._handle.setBlocking(blocking);
5 | }
6 | });
In googling around, I haven't yet figured out what I'm doing wrong. Any help would be appreciated. Thanks!
As the other answer mentioned, this is probably not recommended. You should really stick your database on the backend somewhere.
However it is possible to do this using sql.js. I had no luck with the sqlite3 module.
Here is the example I used: https://github.com/sql-js/react-sqljs-demo
Start by installing Craco. This is needed for bundling the wasm file.
npm install #craco/craco --save
replace your react scripts with Craco scripts in package.json:
"scripts": {
- "start": "react-scripts start",
- "build": "react-scripts build",
- "test": "react-scripts test",
"start": "craco start",
"build": "craco build",
"test": "craco test"
}
Make a craco.config.js file in the root of your project and place this code inside:
module.exports = {
webpack: {
configure:{
// See https://github.com/webpack/webpack/issues/6725
module:{
rules: [{
test: /\.wasm$/,
type: 'javascript/auto',
}]
}
}
}
};
Then install sql.js
npm install sql.js --save
You probably need to do some more backend setup after this, but for now import the package:
import initSqlJs from 'sql.js';
// Required to let webpack 4 know it needs to copy the wasm file to our assets
import sqlWasm from "!!file-loader?name=sql-wasm-[contenthash].wasm!sql.js/dist/sql-wasm.wasm";
I had to disable eslint to get the second import to work. Really not ideal, but eslint throws errors if you try using file-loader. These are the lines to remove in package.json:
"eslintConfig": {
"extends": "react-app"
},
Now call the library!
const response = fetch("example.com/sqlitedatabase");
const buf = await response.arrayBuffer();
const SQL = await initSqlJs({ locateFile: () => sqlWasm });
const db = new SQL.Database(new Uint8Array(buf));
You can then query the database; The following code runs a query and then puts it into a more useable form than the default response.
const query = db.exec(`
SELECT
*
FROM
table;
`);
const columns = query[0].columns;
const values = query[0].values;
let rows = [];
values.forEach(element => {
let row = {};
for (let i = 0; i < columns.length; i++) {
row[columns[i]] = element[i];
}
rows.push(row);
});
That should work, but as I mentioned above, you might need to mess with the backend a little. Often times single page applications have trouble with CORS when dealing with bundled files. To fix this on AWS Amplify, I added the following code in a file called customHttp.yml to the root of my project:
customHeaders:
- pattern: '**/*'
headers:
- key: Access-Control-Allow-Origin
value: '*'
- pattern: '*.wasm'
headers:
- key: Access-Control-Allow-Origin
value: '*'
(Edit: Added a more generic pattern to the file, didn't work without it.)
Note that it may take some time for the custom headers to update, even though it looks like they update immediately.
Also related to single page applications, wasm files need to be added to the list of files exempted from redirects:
Source address: </^((?!.(css|gif|ico|jpg|js|png|txt|svg|woff|ttf|json|map|wasm)$).)*$/>
Target address: /index.html
Type: 200 (Rewrite)
ReactJS is mostly a front end framework, you should create a backend server.
Some NPM packages are not compatible with front-end JS application.
Your react application will call the server and the server manipulate the DB transactions. You can for example use express js. Useful docs below :
ExpressJS - SQLite doc
API Request from react app
I need to proxy requests from a Create React App to a separate API server, and set that server dynamically or with environment variables. I followed configuring proxy manually, however I am using TypeScript. react-scripts-ts does not seem to load src/setupProxy.js even after updating to latest version (v3.1.0). I got it working with vanilla javascript, but am unable to get it to work with TypeScript. Has anyone gotten setupProxy to work with React TypeScript?
After code diving, it appears the typescript create-react-app has not yet incorporated custom proxy functionality. I had to update two files:
https://github.com/samuelstevens9/create-react-app/blob/next/packages/react-scripts/config/paths.js
Added proxySetup: resolveApp('src/setupProxy.js'), to each module.exports, the last (3rd) being proxySetup: resolveOwn('template/src/setupProxy.js'),
https://github.com/samuelstevens9/create-react-app/blob/next/packages/react-scripts/config/webpackDevServer.config.js
Added const fs = require('fs'); below line 15 const paths = require('./paths'); and added
if (fs.existsSync(paths.proxySetup)) {
// This registers user provided middleware for proxy reasons
require(paths.proxySetup)(app);
}
inside the before(app) { ... } function towards the end of the file.
I am working on creating a pull request to the main repo, but it looks like the v3.1.0 files are different from the most up to date ones on the next branch. For now I use a patch script I made since we are using a lerna monorepo that updates all necessary packages:
#!/bin/bash
CONFIG_PATHS_URL="https://raw.githubusercontent.com/samuelstevens9/create-react-app/next/packages/react-scripts/config/paths.js"
CONFIG_WEBPACKDEVSERVER_URL="https://raw.githubusercontent.com/samuelstevens9/create-react-app/next/packages/react-scripts/config/webpackDevServer.config.js"
SETUPPROXY_URL="https://gist.githubusercontent.com/samuelstevens9/5872e72ac915dfc1a8ae2fdcef323899/raw/7f2c76d42bc0915026379dfc7884cb1bd97f56bb/setupProxy.js"
for f in packages/*; do
if [ -d ${f} ]; then
echo $f
# Will not run if no directories are available
NODE_MODULES_CONFIG_DIR=$f/node_modules/react-scripts-ts/config
if [ -d "$NODE_MODULES_CONFIG_DIR" ]; then
# Control will enter here if $DIRECTORY exists.
echo $NODE_MODULES_CONFIG_DIR
curl -o $NODE_MODULES_CONFIG_DIR/paths.js $CONFIG_PATHS_URL
curl -o $NODE_MODULES_CONFIG_DIR/webpackDevServer.config.js $CONFIG_WEBPACKDEVSERVER_URL
curl -o $f/src/setupProxy.js $SETUPPROXY_URL
fi
fi
done
And updates the setupProxy.js file in each package as well. Hope this helps.
Now CRA supports Typescript but I couldn't make setupProxy.js to work.
My mistake was super dumb. setupProxy was outside src/ folder.
So, make sure that you create setupProxy inside the folder src
src/setupProxy.js
My code looks like this:
module.exports = function (app) {
app.use(
'/api',
createProxyMiddleware({
target: process.env.REACT_APP_API_URI,
changeOrigin: true,
})
)
}
Also, make sure that your env configuration is working.
You need to install the package env-cmd and replace
"start": "react-scripts start",
for
"start": "env-cmd -f .env.development.local react-scripts start",