javascriptreactjsprogressive-web-appsservice-workeroffline-caching

React app to work offline not able to cache files after deployment


I'm making react app to work offline for which I'm caching the files in sw.js file which is under public folder. The files are getting cached locally but when I'm deploying the app, it is not caching the files and giving the error - Uncaught (in promise) TypeError: Failed to execute 'addAll' on 'Cache': Request failed. Below is my sw.js file - ` let CACHE_NAME = "codePwa";

var urlCache = \[
"/manifest.json",
"/logo192.png",
"/",
"/index.html",
"/static/js/main.4d5113ea.js",
"/static/css/main.073c9b0a.css",
"./App.js",
\];

/// install service worker
this.addEventListener("install", (event) =\> {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) =\> {
return cache.addAll(urlCache);
})
);
});

// fetch cache data

this.addEventListener("fetch", (event) =\> {
if (!navigator.onLine) {
console.log("offline");
if (event.request.url === "http://localhost:3000/static/js/main.chunk.js") {
event.waitUntil(
this.registration.showNotification("modeNet", {
body: "Offline",
icon: "http://localhost:3000/logo192.png",
})
);
}
event.respondWith(
caches.match(event.request).then((response) =\> {
if (response) {
return response;
}
let fUrl = event.request.clone();
fetch(fUrl);
})
);
}
});

this.addEventListener("activate", function (event) {
event.waitUntil(
caches.keys().then(function (cacheNames) {
return Promise.all(
cacheNames
.filter(function (cacheNames) {
//
})
.map(function (cacheNames) {
return caches.delete(cacheNames);
})
);
})
);
});`

What changes should I make so that after deploying the app, it'll cache the files and when the app is offline and after refreshing the app fetch the file from the cache. I'm deploying my app on Netlify(url - https://calm-salmiakki-411347.netlify.app/)


Solution

  • This error causes when one or more URLs doesn't exist and cannot be found to be cached

    I checked the urls and it seems that ./App.js doesn't exist. if it's the App.js of your react project, you don't need to cache it (because it doesn't exist in production). so remove it from your urlCache array

    and if you are using cache first strategy you need to handle it with cache.open and then cache.match

    so change this part :

    event.respondWith(
      caches.match(event.request).then((response) =\> {
      if (response) {
        return response;
      }
      let fUrl = event.request.clone();
      fetch(fUrl);
    })
    );
    

    to this :

    event.respondWith(
      caches.open(cacheName)
        .then(function(cache) {
          cache.match(event.request)
            .then( function(cacheResponse) {
              if(cacheResponse)
                return cacheResponse
              else
                return fetch(event.request)
                  .then(function(networkResponse) {
                    cache.put(event.request, networkResponse.clone())
                    return networkResponse
                  })
            })
        })
    )
    

    and to be more organized, I rewrote your code

    let CACHE_NAME = "codePwa";
    
    var urlCache = [
        "./manifest.json",
        "./logo192.png",
        "./",
        "./index.html",
        "./favicon.ico",
        "./static/js/main.4d5113ea.js",
        "./static/js/bundle.js",
        "./static/css/main.073c9b0a.css",
        "/about",
        "/contact",
    ];
    self.addEventListener('install', function (evt) {
        console.log('The service worker is being installed.');
        evt.waitUntil(precache());
    });
    
    
    
    function fromNetwork(request, timeout) {
        return new Promise(function (fulfill, reject) {
            var timeoutId = setTimeout(reject, timeout);
            fetch(request).then(function (response) {
                clearTimeout(timeoutId);
                fulfill(response);
            }, reject);
        });
    }
    
    self.addEventListener('fetch', function (evt) {
    
        if (!navigator.onLine) {
            console.log("offline");
            if (evt.request.url === "http://localhost:3000/static/js/main.chunk.js") {
                evt.waitUntil(
                    this.registration.showNotification("modeNet", {
                        body: "Offline",
                        icon: "http://localhost:3000/logo192.png",
                    })
                );
            }
        }
    
         evt.respondWith(
            fromNetwork(evt.request, 400).catch(() => fromCache(evt.request))
        );
        evt.waitUntil(update(evt.request));
    });
    
    function precache() {
        return caches.open(CACHE_NAME).then(function (cache) {
            return cache.addAll(urlCache);
        });
    }
    
    function fromCache(request) {
        return caches.open(CACHE_NAME).then(function (cache) {
            return cache.match(request).then(function (matching) {
                return matching || Promise.reject('no-match');
            });
        });
    }
    
    function update(request) {
        return caches.open(CACHE_NAME).then(function (cache) {
            return fetch(request).then(function (response) {
                return cache.put(request, response);
            });
        });
    }
    
    
    self.addEventListener('activate', evt =>
        evt.waitUntil(
            caches.keys().then(cacheNames => {
                return Promise.all(
                    cacheNames.map(cacheName => {
                        if (cacheName !== CACHE_NAME) {
                            return caches.delete(cacheName);
                        }
                    })
                );
            })
        )
    );