React Native: Why Mobile App is not changed when cache is changed? - reactjs

I use React native.
When I use web with react, if I change cache info then that change is automatically applied on my web screen.
But in case of react native app, if I change cache info(I check cache is correctly changed), my mobile app (expo) doesn't change at all. So I need to refresh my screen once again.
Is that correct? or did I do something wrong?
For example, on react when I run this function to edit cache.
It is written to when I click button, this function runs.
It delete my comment from server then delete this record from cache.
When I run it on web, this deleted comment disappears automatically on web.
But on my app, it has no change at all.
const updateDeleteComment = (cache, result) => {
const {
data: {
deleteComment: { ok, error },
},
} = result;
if (ok) {
cache.evict({ id: `Comment:${id}` });
cache.modify({
id: `Photo:${photoId}`,
fields: {
commentNumber(prev) {
return prev - 1;
},
},
});
}
};

Related

Change the address without refreshing the page

I have a category list, which changes the address of the page by selecting each category, similar to dynamic adress, for example, when you select a game, the address goes to:
/s/game changes
Or you choose real estate
/s/estate
To transfer from the code:
router.push(address), null, {
shallow: true,
scroll: false
})
I use.
But the problem is that the things related to receiving the link only work the first time and the second time only the address changes and getStaticPaths and getStaticProps do not work at all!
The codes inside related to the /s/[category].js file (address recipient file):
export async function getStaticPaths() {
return {
paths: [
{ params: { category: "Search Category" } },
],
fallback: false,
};
}
export async function getStaticProps(context) {
const { params } = context;
return {
props: {
category: params.category,
},
};
}
It means that after compiling the component, the related items will no longer work!
where is the problem from ? Do you know the best method for this? Thank you for telling me
I have said all the things I used in the question
You may try to troubleshoot this issue with hlep of developer tools integrated with your browser Firefox or Chrome then in network tab look at http requests. You are looking for new http get to new uri

Vue Fetch Behaving differently on Local vs. Deployed

I feel that I am implementing Vuex Store or async-await incorrectly.
My goal is to pull a set of badges (we call them patches) from my content management service. I need the list to be up to date whenever a new badge is added in the future (which will be infrequent but not never). I don't know how to make a check that will refresh the list whenever the current badge count is different from the number of badges in the cms but that is beside the current problem.
I have an array, rawPatches, in Vuex that should only be built if rawPatches.length <= 0. Once built it should have 60 items pulled from my content management service. This seems to work fine when I do npm run dev on my local machine. However, once pushed to the development site and compiled through Netlify, the array is messed up. When I visit another page and come back to where the length check is the array has an extra 60 items. So when I leave and come back twice then the array has 180 items, and so on and so forth. Also, when I close the window and then come back the incorrect count remains. I guess this means that the Vuex State is cached? I don't know if the length check doesn't get executed or if the array doesn't exist when the check happens but then does exist when new items are added because I'm awaiting the build function. I really have no idea what is going on but I have been trying to sort it out for a few months and am ripping my hair out.
I am using async-await because readPersonalPatches relies on the patches being in Vuex Store.
index.js
export const state = () => ({
rawPatches: [],
});
export const mutations = {
addToArray: (state, payload) => {
state[payload.arr].push(payload.value);
},
}
export const actions = {
async readPatches({ commit, state }) {
console.log('inside of readPatches', state.rawPatches.length);
const patches = await this.$content('patches').fetch();
patches.forEach((patch) => {
commit('addToArray', { arr: 'rawPatches', value: patch });
if (patch.categories) {
if (
JSON.stringify(patch.categories).includes('orbit') ||
JSON.stringify(patch.categories).includes('point')
) {
commit('addToArray', { arr: 'geographicPatches', value: patch });
}
}
if (patch.isSecret) {
commit('addToArray', { arr: 'secretPatches', value: patch });
}
});
console.log('After adding patches', state.rawPatches);
},
}
header component
async fetch() {
console.log('Does this work?');
try {
console.log('length', this.rawPatches.length <= 0);
if (this.rawPatches.length <= 0) {
await this.readPatches({ context: this.$nuxt.context });
}
this.readPersonalPatches();
} catch (error) {
console.log(error);
}
},
Locally, the console reads:
Does this work?
length true
inside of readPatches 0
after reading patches [...]
However, the console is blank on the dev server.
Thank you for any help with this!!

PWA Push Notification not showing when app is not running

I'm developing a Progressive Web App with React that gets notifications when a new offer has been added to the DB. Everything works fine, I open the web, asks the user to grant permissions to enable notifications, we allow them, install the PWA, run it, add a new offer in the DB, and the a notification with the new offer gets displayed (Chrome + Windows 10).
But the issue is I don't get any notifications if the PWA is not running.. I would have thought the service worker is running in the background even if the PWA is closed. What am I missing?
here is my notifyNewOffer function in my notifications.ts file
function notifyNewOffer(newOffer: Offer) {
if ('serviceWorker' in navigator) {
const options = {
body: newOffer.subheading,
icon: './logo192.png',
image: './static/media/placeholder-offer.1bcbf040.png',
vibrate: [100, 50, 200],
badge: './favicon.ico',
tag: 'new-offers',
renotify: true,
actions: [
{ action: 'confirm', title: 'Check offer', icon: '' },
],
};
navigator.serviceWorker.ready.then(swreg => {
swreg.showNotification(newOffer.heading, options);
});
} else {
console.log('no serviceWorker');
}
}
And this is how I call it:
function addedOfferSubs<T>(setOffers: (offers:any) => void) {
// #ts-ignore
const subscription = API.graphql(graphqlOperation(addedOffer)).subscribe({
next: async (eventData: SubscriptionValue<T>) => {
const newOffer = (eventData.value.data as any).addedOffer;
await indexedDb.createObjectStore('offers', 'id'); // Opens db. Will create the table offers only if it doesnt already exist
await indexedDb.putValue('offers', newOffer); // Adds new offer
// Push notification
notifyNewOffer(newOffer);
// Set offers
const offersData = await getOffersFromIdb();
setOffers(offersData);
},
});
return () => subscription.unsubscribe()
}
Any ideas ?
Thanks very much
In order for notifications to appear when the app isn't open, you'll need to use Web Push as well. Web push allows you to send a notification from your server to device. When the push arrives on the device, it wakes up the service worker, and the notification is shown there.
Instructions for setting up Web Push & Notifications are available at https://developers.google.com/web/fundamentals/push-notifications

How to activate a react route and pass data from the service worker?

I have a SPA PWA React app.
It is installed and running in standalone mode on the mobile device (Android+Chrome).
Let's say the app lists people and then when you click on a person it diplays details using /person route.
Now, I'm sending push notifications from the server and receiving them in the service worker attached to the app. The notification is about a person and I want to open that person's details when the user clicks on the notification.
The question is:
how do I activate the /person route on my app from the service worker
and pass data (e.g. person id, or person object)
without reloading the app
From what I understand, from the service worker notificationclick event handler I can:
focus on the app (but how do I pass data and activate a route)
open an url (but /person is not a physical route, and either way - I want avoid refreshing the page)
You can listen for click event for the Notification which you show to the user. And in the handler, you can open the URL for the corresponding person which comes from your server with push event.
notification.onclick = function(event) {
event.preventDefault();
// suppose you have an url property in the data
if (event.notification.data.url) {
self.clients.openWindow(event.notification.data.url);
}
}
Check these links:
https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerGlobalScope/notificationclick_event
https://developer.mozilla.org/en-US/docs/Web/API/Clients/openWindow
To answer my own question: I've used IndexedDB (can't use localStorage as it is synchronous) to communicate between SW and PWA, though I'm not too happy about it.
This is roughly how my service worker code looks (I'm using idb library):
self.addEventListener('notificationclick', function(event) {
const notif = event.notification;
notif.close();
if (notif.data) {
let db;
let p = idb.openDB('my-store', 1, {
upgrade(db) {
db.createObjectStore(OBJSTORENAME, {
keyPath: 'id'
});
}
}).then(function(idb) {
db = idb;
return db.clear(OBJSTORENAME);
}).then(function(rv) {
return db.put(OBJSTORENAME, notif.data);
}).then(function(res) {
clients.openWindow('/');
}).catch(function(err) {
console.log("Error spawning notif", err);
});
event.waitUntil(p);
}
});
and then, in the root of my react app ie in my AppNavBar component I always check if there is something to show:
componentWillMount() {
let self = this;
let db;
idb.openDB('my-store', 1)
.then(function (idb) {
db = idb;
return db.getAll(OBJSTORENAME);
}).then(function (items) {
if (items && items.length) {
axios.get(`/some-additional-info-optional/${items[0].id}`).then(res => {
if (res.data && res.data.success) {
self.props.history.push({
pathname: '/details',
state: {
selectedObject: res.data.data[0]
}
});
}
});
db.clear(OBJSTORENAME)
.then()
.catch(err => {
console.log("error clearing ", OBJSTORENAME);
});
}
}).catch(function (err) {
console.log("Error", err);
});
}
Have been toying with clients.openWindow('/?id=123'); and clients.openWindow('/#123'); but that was behaving strangely, sometimes the app would stall, so I reverted to the IndexedDB approach.
(clients.postMessage could also be the way to go though I'm not sure how to plug that into the react framework)
HTH someone else, and I'm still looking for a better solution.
I had a similar need in my project. Using your's postMessage tip, I was able to get an event on my component every time a user clicks on service worker notification, and then route the user to the desired path.
service-worker.js
self.addEventListener("notificationclick", async event => {
const notification = event.notification;
notification.close();
event.waitUntil(
self.clients.matchAll({ type: "window" }).then(clientsArr => {
if (clientsArr[0]) {
clientsArr[0].focus();
clientsArr[0].postMessage({
type: "NOTIFICATION_CLICK",
ticketId: notification.tag,
});
}
})
);
});
On your react component, add a new listener:
useEffect(() => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.addEventListener("message", message => {
if (message.data.type === "NOTIFICATION_CLICK") {
history.push(`/tickets/${message.data.ticketId}`);
}
});
}
}, [history]);

How to prevent first route to show on notification click in react-native

I have implemented the push notification using react-native-push-nofication here is my push notification configuration.
const configure = () => {
var _token
PushNotification.configure({
onRegister: function(token) {
//process token
//alert(JSON.stringify(token));
Clipboard.setString(JSON.stringify(token))
},
onNotification: function(notification) {
// process the notification
// required on iOS only
navigator.navigate(notification.data.url);
// notification.finish(PushNotificationIOS.FetchResult.NoData);
},
senderID: Config.GCMSENDERKEY,
permissions: {
alert: true,
badge: true,
sound: true
},
popInitialNotification: true,
requestPermissions: true,
});
};
This code is navigating to the desire route successfully but when the application is in background, when user click on notification it shows the root route of the application (splash screen) before navigating to the desire route. I don't want splash screen to show up at all.
I just reviewed the library and I have to say, it's really bad designed. You should move to react-native-firebase.
But if you want to stay with it:
The first thing we have to figure out, is, how we can get the notification with which the app was opened?
We can't use onNotification from the Library, because we don't know when the callback will be called.
The library has a option popInitialNotification which will throw the initial notification with which the app was opened. If you search for this variable in the source code of the library you will find the following:
if ( this.hasPoppedInitialNotification === false &&
( options.popInitialNotification === undefined || options.popInitialNotification === true ) ) {
this.popInitialNotification(function(firstNotification) {
if ( firstNotification !== null ) {
this._onNotification(firstNotification, true);
}
}.bind(this));
this.hasPoppedInitialNotification = true;
}
As you can see, it will call a function named popInitialNotification(callback) with a callback function with the initial notification.
You can now use the function to get the initial notification.
PushNotification.popInitialNotification(function(notification) {
});
With this you have now access to the initial notification directly, without to wait for onNotification.
From here on you can use SwitchNavigator from react-navigation like in the following example: https://reactnavigation.org/docs/en/auth-flow.html

Resources