React - Unregistering a registered service worker - reactjs

I'm developing a React web-app (https://timatorg.is - in case someone wants to look at the response headers) created using create-react-app. At the time it was created, a service worker was registered by default and I didn't really think much about it. The app is hosted at Netlify and the DNS is at Cloudflare.
Now when deploying updates to the website, users that have visited the site before always get the same version of the app and I'm pretty sure it's because of the service worker. But the strange thing is that it's only "https://www.timatorg.is" that is causing issues, not "https://timatorg.is"
I've read a lot of material about how to unregister an already cached service worker but all I've found is a solution that the client can perform in his/her browser but nothing that I can do to solve this for all of our clients.
So my question is, how do I get rid of the service worker? Or more general, push the latest update of our app to our clients?

You can change to the following in your index.js file, like the comment in a new Create React App project says:
import { unregister } from './serviceWorker';
// ...
unregister();

Add the following code into your index.js file:
if ('serviceWorker' in navigator) {
navigator.serviceWorker.ready.then(registration => {
registration.unregister();
if (caches) {
// Service worker cache should be cleared with caches.delete()
caches.keys().then(async (names) => {
await Promise.all(names.map(name => caches.delete(name)));
});
}
});
}

Related

How to connect a React frontend on Netlify to a Flask backend on PythonAnywhere

TLDR: React app interfaces properly with Flask API on PythonAnywhere when hosted locally but not when a static build is hosted on Netlify. Perhaps the proxy information is missing from the build?
EDIT 1:
Here are the errors in the browser console:
I've created a Flask API that pulls machine learning models from Amazon S3 and returns predictions on data input from POST requests. I've put this API on PythonAnywhere.
I've also created a React frontend which allows me to input data, submit it, and then receive the prediction. When I host this app locally, it behaves appropriately (i.e. connecting to the Flask app on PythonAnywhere, loading the models properly, and returning the predictions).
I've tried deploying a static build of the React app on Netlify. It behaves as expected, except for anything that requires interacting with the Flask App. I have a button for testing that simply calls the Flask app in a GET request, and even this is throwing a 404 error.
I checked the error and server logs on PythonAnywhere and see nothing. The only thing I can thik of is that my proxy which lists the domain of the PythonAnywhere app in my package.json file is for some reason unincluded in the build, but I don't know why this would be the case.
Has anyone else run into a similar issue or know how I can check to see if the proxy information is included in the static build? Thanks in advance!
Thanks to #Glenn for the help.
Solution:
I realized (embarrassingly late) that the requests were not going to the right address, as can be seen in the browser console error above. I was using a proxy during development, so the netlify app was calling itself rather than the pythonanywhere API. I simply went into my react code and edited the paths to pythonanywhere. E.g.
onClick={ async () => {
const response = await fetch("/get", {...}}
became
onClick={ async () => {
const response = await fetch("https://username.pythonanywhere.com/get", {...}}
As #Glenn mentioned, there may have been a CORS issue as well, so in my flask application I utilized flask_cors. I can't say for sure that this was necessary given that I didn't test removing it after the fetch addresses had changed, but I suspect that it is necessary.
Hopefully this can help someone else

A website is not refreshing because of caching of service worker, after switching from React to Next.js. How to force update?

A website made on react had a service worker for caching. We ran the website with unregister() code and the website was ported to Next.js . Although on some machines, the unregister code never ran, so now those machines are running the old react code and not reflecting any changes made and pushed to the server. The changes are running on incognito and other machines where the unregister code ran. It is on Caddy server.
How can I force it to refresh from server side? We can clear cache or unregister the service worker on our machine but we don't know how many clients have the old code.
Thanks!
So the issue has been solved. Made a new service worker file, with just the code to remove the service worker.
if ("serviceWorker" in navigator) {
window.addEventListener("load", function () {
navigator.serviceWorker.getRegistrations().then((registrations)
=> {
for (let registration of registrations) {
registration.unregister().then((bool) => {
console.log("unregister: ", bool);
});
}
});
});
}
And put this file in the exact location where the previous service worker was kept (You can find this out from the chrome developer tools). Also include the script tag linking this file in index.html or _document.js (for next.js). This should resolve the issue.
To solve this, the idea is to unregister the Service Worker of our React app so the browser can remove it. To do that, we have to create a Service Worker on the root of the Next.js project and add an EventListener — on any loaded page — to remove this file from the browser. Once we do this, we can be sure that next time a browser makes a request, it will be updated. Another important thing to note is that browsers need to close all the Service Worker’s instances associated with our app to apply the changes.
public/service-worker.js
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.getRegistrations().then(registrations => {
for(let registration of registrations) {
registration.unregister().then(bool => {console.log('unregister: ', bool);});
}
window.location.reload();
});
});
}
Source: https://www.asapdevelopers.com/service-worker-issue-nextjs-framework/

TypeError: firebase.default.messaging is not a function (in '_firebase.default.messaging()','firebase.default.messaging is undefined)

I want to send notifications using firebase cloud functions so I am trying to get a token using firebase.messaging().getToken() but I keep getting the error:
TypeError: firebase.default.messaging is not a function (in '_firebase.default.messaging()','firebase.default.messaging is undefined)
I have installed firebase and firebase/messages about 5 different ways but cannot seem to get past this error so I assume that those methods must be outdated or I am doing something very wrong.
Here is my code:
import * as firebase from 'firebase'
const Firebase = firebase.initializeApp(firebaseConfig);
export default Firebase
and here is where I am getting the error:
Token : Firebase.messaging().getToken()
I am not sure if I am missing some dependency or anything of that sort so any help would be very appreciated. I have been installing things straight from the firebase documentation as web which has been working so far.
I am also using expo managed react native
Thank you
Also is there some alternative way to send notifications in the background with an event listener that may be better?
It's been a while since you posted, but I've gone through the same error so I will share some findings about it. The solution you are looking for is bullet number 3.
1) Firebase JavaScript SDK is meant for Node.js applications. Even though the firebase sdk package can be installed and used straightaway just like any other npm package in a react-native/expo project, it's meant to run on a browser or node.js runtime.
It's specially confusing because some of the firebase Api methods (e.g. firebase.auth and firebase.database) do work on native apps while others don't. I guess that's due to the fact that some methods only rely on the hability to perform HTTP requests.
In deed the firebase sdk official documentation page doesn't have a react-native section. If you check out the JavaScript section, and click on Add Firebase to your JavaScript Project you will see that the intended uses comprehend web apps and node.js desktop apps (not sure which runtime IoT apps run on):
If you still want to use the firebase sdk you should then consider libraries such as React Native Firebase which aims to solve the issues of using the JavaScript sdk outside a node.js runtime. I haven't tried myself but having a look at their website, looks like it's not going to be easy to integrate in the expo managed workflow.
2) Importing #firebase/messaging is not an option either: As suggested in this answer, explicitly importing the messaging module will not solve the issue for react-native/expo projects. Again, as described above, because native apps do not run on a node.js/browser runtime. In fact, it makes things worst because it tries to load dependencies that aren't available:
[09:56:33] ReferenceError: Can't find variable: IDBIndex
3) Getting the user device notifications push token in Expo: Since you are already using expo, you can use the expo-notifications module to get the device push token, which will return the token you then can use from Cloud Functions to send a notification through the firebase-admin sdk. Here is some sample code to get the push token and storing it in the realtime database (to be later used from the cloud functions):
import * as Notifications from 'expo-notifications';
import * as Permissions from 'expo-permissions';
import firebase from 'firebase';
export const getDevicePushToken = () => {
return Permissions.getAsync(Permissions.NOTIFICATIONS)
.then((response) =>
response.status === 'granted'
? response
: Permissions.askAsync(Permissions.NOTIFICATIONS)
)
.then((response) => {
if (response.status !== 'granted') {
return Promise.reject(new Error('Push notifications permission was rejected'));
}
return Notifications.getDevicePushTokenAsync();
})
.then(token => {
firebase.database().ref('...').update({ pushToken: token.data });
})
.catch((error) => {
console.log('Error while registering device push token', error);
});
};
Cheers!
I was getting similar message when I as trying to use jest to write unit tests and mocking firebase-admin.
I am writing my version in case it helps someone.
I was facing error while mocking firebase-admin.
I jest-mocked firebase-admin like below:
jest.mock('firebase-admin');
But I started getting following error:
firebase_admin_1.default.messaging is not a function
It was coming because in the app code, I had used firebase-admin like this:
await admin.messaging().send(message)
(message is an instance of TokenMessage)
The problem was that I was not mocking the member variables and member methods. Here it was the member method messaging and a nested member method within messaging called send. (See that messaging method is called on admin and then send is called on the value of admin.messaging()). All that was needed was to mock these like below:
jest.mock('firebase-admin', () => ({
credential: {
cert: jest.fn(),
},
initializeApp: jest.fn(),
messaging: jest.fn().mockImplementation(() => {
return {
send: jest
.fn()
.mockReturnValue('projects/name_of_project/messages/message_id'),
};
}),
}));
(Note that I have also mocked other member variables/methods as per my original requirement. You would probably need these mocks as well)
Firebase updated itself for react native version 0.6 +
Below solution will work.
import { firebase } from '#react-native-firebase/messaging';
await firebase.messaging().registerDeviceForRemoteMessage();
firebase.messaging().getToken();
Solution 2:-
import messaging from '#react-native-firebase/messaging';
await messaging(). registerDeviceForRemoteMessage ();
await messaging().getToken();

Unknown service worker is being registered

I am using the built in service worker found in create-react-app. I have registered it in index.tsx with serviceWorker.register();. If I create a production build and run it, or run it on heroku server, I successfully get a service worker registered. However, upon further inspection under the Application tab in the dev tools, I see service-worker.js as the name of my service worker, which is NOT what mine is called. Inspecting the worker, it is also very different code from that of the default create-react-app, the contents are below -
/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app and you should
* disable HTTP caching for this file too.
* See https://developers.google.com/web/tools/workbox/guides/service-worker-checklist
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://developers.google.com/web/tools/workbox/modules/workbox-build#full_generatesw_config
*/
importScripts("https://storage.googleapis.com/workbox-cdn/releases/4.3.1/workbox-sw.js");
importScripts(
"/precache-manifest.740fac3abc44b443a1d2c6ac9789a1e1.js"
);
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
workbox.core.clientsClaim();
/**
* The workboxSW.precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://developers.google.com/web/tools/workbox/modules/workbox-precaching
*/
self.__precacheManifest = [].concat(self.__precacheManifest || []);
workbox.precaching.precacheAndRoute(self.__precacheManifest, {});
workbox.routing.registerNavigationRoute(workbox.precaching.getCacheKeyForURL("/index.html"), {
blacklist: [/^\/_/,/\/[^\/]+\.[^\/]+$/],
});
Meanwhile the create-react-app service worker should look like this.
I am wondering where this other service worker is coming from, and how can I make sure my own is registered instead?
I haven't read them both, but it seems like maybe yours was compiled/transpiled by webpack into that one.
https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/package.json
That service worker cdn is workbox v4.3.1 which you can see in the above package.json is a dependency. It is then used in a webpack config file.
Typescript is a way to write JavaScript, but it needs to be changed to JavaScript before it can be run in most cases.
Edit: where it's used
https://github.com/facebook/create-react-app/blob/589b41aecaa10d410713794f66a648bf3a72fb62/packages/react-scripts/config/webpack.config.js#L674
https://create-react-app.dev/docs/making-a-progressive-web-app/
And in conclusion: serviceWorker.ts is not a service worker. It is used to register one. The one you see on Heroku? That is the service worker you registered for, create-react-app's default one.
In my case:
I was using react.js and create-react-app.
I adopted new service-worker.js in public/
and my problem was that when I used yarn build to build react app, my service-worker.js is replaced with above file you copied(workbox service worker)
So I just made new file (public/worker.js) and replace
const swUrl = "${process.env.PUBLIC_URL}/service-worker.js";
with
const swUrl = "${process.env.PUBLIC_URL}/worker.js";
in src/serviceWorker.js > register > window.addEventListener('load')
and finally I can register my own service worker to my app

ServiceWorker is not updating, goes to skipwaiting in React app

When I make a new production build of React app, new services worker installs but goes to skipwaiting state. So I have to hard reset cache on every build that is not good on live site.
I can't tell to every client, "please clear your cache to see latest". If I remove registerServiceWorker() from index file then build production do not detect my routes so it redirects to 404.
I used react-rewired-app boilerplate for my project and faced issue in that. Now I resolved that with a bunch of code in config-overrides.js file
config.plugins.forEach((obj) => {
if (obj.config) {
if (obj.config.clientsClaim) {
obj.config.skipWaiting = true
}
}
});

Resources