Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React w/ Service Worker & Push Notifications

Some initial considerations:

"react": "^16.8.2",
"react-scripts": "2.1.5"

I have created a new react app and I need to implement Push Notifications. Following this tutorial, I was able to get up and running in 5 minutes, but now I have to implement the same strategy (kinda) into a react app.

The problem I am facing is that I am able to subscribe to the Notification API, but I'm not sure how to edit the service-worker.js file to add an event listener to catch the push event (Handle a Push Event chapter in the google guide)

like image 282
Mattia M. Avatar asked Feb 19 '19 15:02

Mattia M.


People also ask

How do you use a service worker in React?

A service worker is generated by default when we use the create-react-app to create a new React project. We will find the serviceWorker. js file in the src folder of the project. But the service worker is not functional out of the box; we will have to configure it.

Can you make PWA With React?

For your needs, you can take advantage of npm to create a React application with a PWA template, which allows you to code right away. Whenever you start building a React app, you can use templates offered by Facebook using the 'create-react-app' command with npm.

Can we connect React with SAP?

Use the CData Connect and React to Build Dynamic Web Apps with Live SAP Business One Data. React is a declarative, efficient, and flexible JavaScript library for building user interfaces.


2 Answers

Customizing your service worker with Create React App is possible, but could be quite difficult and hacky.

Out of the box, CRA uses Workbox GenerateSW webpack plugin to generate service-worker.js file, and you cannot inject any code to it (you could with CRA@1, not any more with since CRA@2)

You have several strategies, I'll start with the simplest one.

Solution 1: provide your own service-worker file

  • in src/index.js enable service worker:
    // serviceWorker.unregister()
    serviceWorker.register()
    
  • in src/serviceWorker.js register your custom file:

    // if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) {
    if ('serviceWorker' in navigator) {
    
    // const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`;
    const swUrl = `${process.env.PUBLIC_URL}/custom-service-worker.js`;
    

    You have to change the name cause when running dev server, CRA provides a mock for service-worker.js

  • in public/ folder, create custom-service-worker.js file. Webpack will copy it as is in the build/ folder

Pros: quick, dirty win

Cons: your custom file is not processed with Webpack (no imports), and you must implement the network caching logic by yourself (assuming you want a PWA) since you're bypassing Workbox plugins

Solution 2: append your code to generated service-worker

There's a module for it: cra-append-sw. You're in charge to provide the appended code.

Pros: easy setup, takes advantages GenerateSW

Cons: appended code is processed with Babel/Webpack, but not using CRA's config (you could opt-out). Still use GenerateSW which handle network caching for you. Not sure it works when developing locally

Solution 3: use Workbox in custom service-worker file

  • apply the first 2 steps of solution #1: change src/index.js and src/serviceWorker.js

  • in src/ folder, create custom-service-worker.js file. It will be processed by Webpack, so you can use ES2016/TypeScript syntax and import modules

    /* eslint no-restricted-globals: "off" */
    import * as precaching from 'workbox-precaching'
    // your own imports
    
    if (self.__precacheManifest) {
    precaching.precacheAndRoute(self.__precacheManifest)
    }
    
    // your own code
    
  • install react-app-rewire:

    • npm add --save-dev react-app-rewired
    • in package.json, in "scripts", replace react-scripts with react-app-rewired
  • tweak webpack configuration: create config-overrides.js in root folder:

    const WebpackBeforeBuildPlugin = require('before-build-webpack')
    const WorkboxWebpackPlugin = require('workbox-webpack-plugin')
    const path = require('path')
    const merge = require('lodash.merge')
    const fs = require('fs')
    
    // from https://www.viget.com/articles/run-multiple-webpack-configs-sequentially/
    class WaitPlugin extends WebpackBeforeBuildPlugin {
    constructor(file, interval = 100, timeout = 60e3) {
     super(function(stats, callback) {
       const start = Date.now()
    
       function poll() {
         if (fs.existsSync(file)) {
           callback()
         } else if (Date.now() - start > timeout) {
           throw Error(`Couldn't access ${file} within ${timeout}s`)
         } else {
           setTimeout(poll, interval)
         }
       }
       poll()
     })
    }
    }
    
    const swOutputName = 'custom-service-worker.js'
    const workerSource = path.resolve(__dirname, 'src', swOutputName)
    
    module.exports = {
    webpack: (config, env) => {
     // we need 2 webpack configurations:
     // 1- for the service worker file.
     //    it needs to be processed by webpack (to include 3rd party modules), and the output must be a
     //    plain, single file, not injected in the HTML page
     const swConfig = merge({}, config, {
       name: 'service worker',
       entry: workerSource,
       output: {
         filename: swOutputName
       },
       optimization: {
         splitChunks: false,
         runtimeChunk: false
       }
     })
     delete swConfig.plugins
    
     // 2- for the main application.
     //    we'll reuse configuration from create-react-app, without a specific Workbox configuration,
     //    so it could inject workbox-precache module and the computed manifest into the BUILT service-worker.js file.
     //    this require to WAIT for the first configuration to be finished
     if (env === 'production') {
       const builtWorkerPath = path.resolve(config.output.path, swOutputName)
       config.name = 'main-application'
       config.plugins.push(
         new WorkboxWebpackPlugin.InjectManifest({
           swSrc: builtWorkerPath,
           swDest: swOutputName
         }),
         new WaitPlugin(builtWorkerPath)
       )
     }
    
     // remove Workbox service-worker.js generator
     const removed = config.plugins.findIndex(
       ({ constructor: { name } }) => name === 'GenerateSW'
     )
     if (removed !== -1) {
       config.plugins.splice(removed, 1)
     } 
    
     const result = [swConfig, config]
     // compatibility hack for CRA's build script to support multiple configurations
     // https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/scripts/build.js#L119
     result.output = { publicPath: config.output.publicPath }
     return result
    }
    }
    

Pros: you can use ES2016/TypeScript code in service-worker file. You still benefit from Workbox network caching facilities, with total control on it

Cons: complicated and fragile, because of the multiple configuration hack.

I've used the last solution, cause I needed both caching code from Workbox and some import in my service worker file.

react-app-rewire-workbox may help simplifying the Webpack configuration (the one for main app). To be tested.

like image 96
Feugy Avatar answered Oct 19 '22 23:10

Feugy


Create React App 4 - Released Oct 2020

Starting with Create React App 4, you have full control over customizing the logic in this service worker, by creating your own src/service-worker.js file, or customizing the one added by the cra-template-pwa (or cra-template-pwa-typescript) template. You can use additional modules from the Workbox project, add in a push notification library, or remove some of the default caching logic.

https://create-react-app.dev/docs/making-a-progressive-web-app/#customization

like image 24
Grazerbeam Avatar answered Oct 19 '22 22:10

Grazerbeam