How to cache a React app with service workers - reactjs

Im just wondering how to handle this. I want to have my whole app cached.
Im have tried something like this which doesnt seem to work
self.addEventListener('install',(e)=>{
console.log('installed');
})
self.addEventListener('activate',(e)=>{
console.log('activated');
self.skipWaiting();
})
self.addEventListener('fetch',(e)=>{
e.respondWith(
fetch(e.request)
.then(res=>{
const resClone = res.clone();
caches.open(cacheName).then(cache=>{
cache.put(e.request, resClone);
})
return res;
}).catch(err => {
console.log('no connection');
caches.match(e.request).then(res => { return res })
})
)
})
Does anyone know how to approach this?

Like one childish way would be to view the page source and check what js, css files are being used by react and cache them manually.
This will not work in production, you will have to manually check the files in the build directory and update the service-worker
Or a better and sensible way of doing it would be to use workbox (a npm package from google) which is going to handle all this clutter

This works for me,
self.addEventListener("fetch", function(event) {
event.respondWith(
caches.match(event.request).then(function(response) {
if (response) {
return response;
} else {
return fetch(event.request)
.then(function(res) {
return caches.open(CACHE_NAME).then(function(cache) {
cache.put(event.request.url, res.clone());
return res;
});
})
}
})
);
});
This tells the service worker to read from cache first and then network if there is no response from cache.
One thing to keep in mind, this will not check for any updates made to the files. If you change your react code, the service worker will load the previous files it has in cache.
To solve this you can use workbox's staleWhileRevalidate which updates the cache whenever there is network connection.
A less convenient solution would be to delete the cache on service worker activation:
self.addEventListener('activate', function(event) {
event.waitUntil(
caches.keys()
.then(function(keyList) {
return Promise.all(keyList.map(function(key) {
return caches.delete(key);
}));
})
);
return self.clients.claim();
});
Whenever a new service worker is installed the cache is removed and a new one is created.

Related

One call to contract function generates multiple transactions

I am adding functionality to the Truffle Pet Shop example dApp. I have created a returnPet function in the solidity contract, and have tested it via console and test contracts. I would now like to call it in my JS app.
The problem is, when I call it, the handleReturn() function,
handleReturn: function(event) {
event.preventDefault();
var petId = parseInt($(event.target).data('id'));
var adoptionInstance;
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
var account = accounts[0];
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
return adoptionInstance.returnPet(petId);
}).then(function(result) {
return App.markAdopted();
}).catch(function(err) {
console.log(err.message);
});
});
generates multiple transactions, and does not affect the blockchain (a different problem. If you have answers to this, I would love to hear them.). As far as I can tell, handleReturn() only gets called once, so why is it generating multiple transactions?
This should work:
handleReturn: function(event) {
event.preventDefault();
var petId = parseInt($(event.target).data('id'));
var adoptionInstance;
web3.eth.getAccounts(function(error, accounts) {
if (error) {
console.log(error);
}
var account = accounts[0];
});
App.contracts.Adoption.deployed().then(function(instance) {
adoptionInstance = instance;
})
adoptionInstance.returnPet(petId).then(function(result) {
return App.markAdopted();
}).catch(function(err) {
console.log(err.message);
});
It calling multiple transactions because you are calling it under the web3.eth.getAccounts methods,if it doesn't work call your contract methods/fuctions using truffle by importing your contracrt artifacts and use webpack to bundle it

How do i cache api in reactjs Progressive Web App(Pwa) to add Offline support?

I have registered my service worker by adding serviceWorker.register(); in my code.In create-react-app Api results are not cached.How do we cache Api result so we counld improve Offline support.
you can cache the network responses and can be served from it as below
window.addEventListener('fetch', function(event) {
event.respondWith(
caches.open('your-app').then(function(cache) {
return cache.match(event.request).then(function (response) {
return response || fetch(event.request).then(function(response) {
cache.put(event.request, response.clone());
return response;
});
});
})
);
});
resource

How can I access Google Fit data without logging in

I have a web page I am working on, I am wanting to use gapi to access my steps for the day. I do not want to log in every time, I want the page to log in automatically in the background, oAuth2 is required I believe but I can not get any further.
Based on code I've found online and using React with Hooks, I added ts ignore because it could not resolve gapi.
The issue I am having is that I get Login Required error
I've tried using Axios and doing it by API_KEY only. My knowledge on API's is growing but I thought just to access the data an API key would be enough providing I had registered the key in the API tools.
React.useEffect(() => {
const start = () => {
// 2. Initialize the JavaScript client library.
// #ts-ignore
gapi.client.init({
'apiKey': '<API_KEY>',
// clientId and scope are optional if auth is not required.
'clientId': '<CLIENT_ID>.apps.googleusercontent.com',
}).then(function() {
// 3. Initialize and make the API request.
// #ts-ignore
return gapi.client.request({
'path': 'https://www.googleapis.com/fitness/v1/users/<MY_GMAIL_ADDRESS>/dataset:aggregate',
'method': 'POST',
'body': {
"aggregateBy": [{
"dataTypeName": "com.google.step_count.delta",
"dataSourceId": "derived:com.google.step_count.delta:com.google.android.gms:estimated_steps"
}],
"bucketByTime": { "durationMillis": 86400000 },
"startTimeMillis": 1567983600000,
"endTimeMillis": 1568057160150
},
})
}).then(function(response) {
console.log(response.result);
}, function(reason) {
console.log('Error: ' + reason.result.error.message);
});
};
// #ts-ignore
gapi.load('client', start);
}, []);
I just want to be able to return my steps in a JSON object preferably with Axios, if not using the gapi JS library using my React Application. Nothing fancy.
I appreciate this may not be possible but the docs and other questions on stack overflow just are not working for me and no one can answer me.

How to cache React application in service worker

I'm trying to create a React PWA from scratch. So far my project outputs the minified files to a dist/js folder.
In my service worker file I'm using Workbox to precache the app. This is my setting so far:
importScripts("./node_modules/workbox-sw/build/workbox-sw.js");
const staticAssets = [
"./",
"./images/favicon.png",
]
workbox.precaching.precacheAndRoute(staticAssets);
Currently if I enable offline from dev tools > Service Workers, it throws these errors and the app fails to load:
3localhost/:18 GET http://localhost:8080/js/app.min.js net::ERR_INTERNET_DISCONNECTED
localhost/:1 GET http://localhost:8080/manifest.json net::ERR_INTERNET_DISCONNECTED
3:8080/manifest.json:1 GET http://localhost:8080/manifest.json net::ERR_INTERNET_DISCONNECTED
logger.mjs:44 workbox Precaching 0 files. 2 files are already cached.
5:8080/manifest.json:1 GET http://localhost:8080/manifest.json net::ERR_INTERNET_DISCONNECTED
How can I fix this?
this means your resources are not getting cached properly,
you need to add them to cache before accessing,
workbox by default do it for you.
it shows 2 files cached, as they present in your array, expected result
same do it for all remaining too.
const staticAssets = [
"./",
"./images/favicon.png",
"./js/app.min.js",
"./manifest.json",
{ url: '/index.html', revision: '383676' }
]
you can try to add eventlistener,
self.addEventListener('install', event => {
console.log('Attempting to install service worker and cache static assets');
event.waitUntil(
caches.open("staticCacheName")
.then(cache => {
return cache.addAll(staticAssets);
})
);
});
self.addEventListener('fetch', function(event) {
event.respondWith(caches.match(event.request)
.then(function(response) {
if (response) {
return response;
}
return fetch(event.request);
})
);
});

Respond always with index.html

React app (usually) uses same index.html for all URLs and that's what my server responds with.
However, first request is never example.com/index.html, it's for exampleexample.com/, example.com/posts, example.com/post/123, example.com/contact and so on..
If I turn on offline mode from Chrome DevTools, I just get default No Connection page.
How to always respond with index.html from cache?
Relevant code:
self.addEventListener('install', function(e) {
self.skipWaiting()
e.waitUntil(
caches.open('v1').then(function(cache) {
return cache.addAll([
'index.html',
'main.js',
'main.css'
])
})
)
})
self.addEventListener('fetch', function(e) {
e.respondWith(
caches.match(e.request).then(function(match) {
// If no match in cache, send request
return match || fetch(e.request)
})
)
})
Im using localhost but I couldn't find any information that it matters when it comes to this problem.
It's because you've explicitly trying to open only cache hits from the cache (caches.match(e.request).then... in your fetch listener). So it will only match the URLs you have manually added to the cache.
To respond for all requests with the pre-cached value, you'd need to explicitly look for index.html cache entry, something like this:
self.addEventListener('fetch', function(e) {
var indexRequest = new Request('/index.html');
// route index.html-based URLs to the specific cache directly
if (shouldRespondWithIndex(e.request.url)) {
e.respondWith(caches.match(indexRequest))
} else {
// other URLs may go through the standard "look for exact match
// in the cache with network fallback" route
e.respondWith(
caches.match(e.request).then(function(match) {
// If no match in cache, send request
return match || fetch(e.request)
}))
}
})
Note that your shouldRespondWithIndex implementation should return false for all non-document requests, i.e. images, stylesheets etc., otherwise the Service Worker will replace it with index.html, too.
You need to change this part of your code:
caches.match(e.request).then(function(match) {
// If no match in cache, send request
return match || fetch(e.request)
})
To return index.html given the conditions that you want. You can find more in the cache documentation.
https://developer.mozilla.org/en-US/docs/Web/API/Cache
To respond to the visitor and avoid that offline screen, the part you have to decide how to handle is how to check event.request to see if returning index.html is good, otherwise it might return that even when you don't want to. You have to use event.respondWith, manually open the cache, find the cache element you want and return it. So instead of looking for a match to event.request, find a match to index.html like this:
event.respondWith(
// Opens Cache objects that start with 'font'.
caches.open(CURRENT_CACHES['font']).then(function(cache) {
return cache.match('/index.html').then(function(response) {
if (response) {
console.log(' Found response in cache:', response);
return response;
}
}).catch(function(error) {
// Handles exceptions that arise from match() or fetch().
console.error(' Error in fetch handler:', error);
throw error;
});
})
);

Resources