Recursive method is calling again and again - android

const callAFunction = () => {
if (AppState.currentState === 'background') {
function1()
}
}
useEffect(()=>{
AppState.addEventListener('change', callAFunction);
},[])
const function1 = () => {
axios.get('/user_login', {
params: {
username: 'john1904',
}
})
.then(function (response) {
if (response.data.status === false) {
function1()
}
})
}
I am using this above function recursively. But as the app goes background function1 is calling again and again as still the function1() i have already called. So i want that function1() call every time as the app goes background. But in Async form as if function1() then it will not call it again.
So i am not able to get how can i do this in when app is in background so it will check if this function is running then don't run it other wise run it.

Right now, if status is false, the only time between requests is the time it takes Axios to call the endpoint. This can be very little time (like a few milliseconds). If you want to poll until you get a status of true, set a timeout for the request.
// ...
.then(function (response) {
if (response.data.status === false) {
setTimeout(function1, 1000);
}
})
The above example will take 1 second between requests. You can adjust 1000 to suit your needs.
See also this question for a common issue with timeouts and React components: Can't perform a React state update on an unmounted component

Related

React PWA application blank page when open it with android/ios

After creating a PWA with react every thing works perfectly except when i want to open the app with a mobile device android/ios....it displays a white screen with no informations.
/////
The app is deployed with IIS manager
my service **service-worker.ts
**
`
/// <reference lib="webworker" />
/* eslint-disable no-restricted-globals */
// This service worker can be customized!
// See https://developers.google.com/web/tools/workbox/modules
// for the list of available Workbox modules, or add any other
// code you'd like.
// You can also remove this file if you'd prefer not to use a
// service worker, and the Workbox build step will be skipped.
import { clientsClaim } from 'workbox-core';
import { ExpirationPlugin } from 'workbox-expiration';
import { precacheAndRoute, createHandlerBoundToURL } from 'workbox-precaching';
import { registerRoute } from 'workbox-routing';
import { StaleWhileRevalidate } from 'workbox-strategies';
declare const self: ServiceWorkerGlobalScope;
clientsClaim();
// Precache all of the assets generated by your build process.
// Their URLs are injected into the manifest variable below.
// This variable must be present somewhere in your service worker file,
// even if you decide not to use precaching. See https://cra.link/PWA
precacheAndRoute(self.__WB_MANIFEST);
// Set up App Shell-style routing, so that all navigation requests
// are fulfilled with your index.html shell. Learn more at
// https://developers.google.com/web/fundamentals/architecture/app-shell
const fileExtensionRegexp = new RegExp('/[^/?]+\\.[^/]+$');
registerRoute(
// Return false to exempt requests from being fulfilled by index.html.
({ request, url }: { request: Request; url: URL }) => {
// If this isn't a navigation, skip.
if (request.mode !== 'navigate') {
return false;
}
// If this is a URL that starts with /_, skip.
if (url.pathname.startsWith('/_')) {
return false;
}
// If this looks like a URL for a resource, because it contains
// a file extension, skip.
if (url.pathname.match(fileExtensionRegexp)) {
return false;
}
// Return true to signal that we want to use the handler.
return true;
},
createHandlerBoundToURL(process.env.PUBLIC_URL + '/index.html')
);
// An example runtime caching route for requests that aren't handled by the
// precache, in this case same-origin .png requests like those from in public/
registerRoute(
// Add in any other file extensions or routing criteria as needed.
({ url }) =>
url.origin === self.location.origin && url.pathname.endsWith('.png'),
// Customize this strategy as needed, e.g., by changing to CacheFirst.
new StaleWhileRevalidate({
cacheName: 'images',
plugins: [
// Ensure that once this runtime cache reaches a maximum size the
// least-recently used images are removed.
new ExpirationPlugin({ maxEntries: 50 }),
],
})
);
// This allows the web app to trigger skipWaiting via
// registration.waiting.postMessage({type: 'SKIP_WAITING'})
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
// Any other custom service worker logic can go here.
const CACHE_NAME = 'cache_sample';
const urlsToCache = ['index.html', 'offline.html'];
const version = 'v0.0.1';
//install sw at first time
//place to cache assets to speed up the loading time of web page
self.addEventListener('install', (event: any) => {
console.log('sw install event');
event.waitUntil(
caches.open(version + CACHE_NAME).then((cache) => {
console.log('opened cache');
return cache.addAll(urlsToCache);
})
);
});
//Activate the sw after install
//Place where old caches are cleared
self.addEventListener('activate', (event: any) => {
console.log('sw activate event');
event.waitUntil(
caches.keys().then((cacheNames) =>
Promise.all(
cacheNames
.filter((cacheName) => {
return cacheName.indexOf(version) !== 0;
})
.map(function (cachName) {
return caches.delete(cachName);
})
)
)
);
});
//listen for requests
self.addEventListener('fetch', (event: any) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
// Any other custom service worker logic can go here.
`
PS : My start_url : "/" in the manifest.json and i've already set the basename attribute in my Router

Create a custom notification with Actions Buttons

import BackgroundService from 'react-native-background-actions';
const sleep = (time) => new Promise((resolve) => setTimeout(() => resolve(), time));
// You can do anything in your task such as network requests, timers and so on,
// as long as it doesn't touch UI. Once your task completes (i.e. the promise is resolved),
// React Native will go into "paused" mode (unless there are other tasks running,
// or there is a foreground app).
const veryIntensiveTask = async (taskDataArguments) => {
// Example of an infinite loop task
const { delay } = taskDataArguments;
await new Promise( async (resolve) => {
for (let i = 0; BackgroundService.isRunning(); i++) {
console.log(i);
await sleep(delay);
}
});
};
const options = {
taskName: 'Example',
taskTitle: 'ExampleTask title',
taskDesc: 'ExampleTask description',
taskIcon: {
name: 'ic_launcher',
type: 'mipmap',
},
color: '#ff00ff',
linkingURI: 'yourSchemeHere://chat/jane', // See Deep Linking for more info
parameters: {
delay: 1000,
},
};
await BackgroundService.start(veryIntensiveTask, options);
await BackgroundService.updateNotification({taskDesc: 'New ExampleTask description'}); // Only Android, iOS will ignore this call
// iOS will also run everything here in the background until .stop() is called
await BackgroundService.stop();
I am using react-native-background-actions. I need 2 buttons in this. But I don’t have any good experience with native code in react native so let me know how can I add custom buttons In it and changes buttons according condition. How can I make changes in the native android for notification buttons.

Appstate keep on getting change in React native in Android

I am working on React native project and there I am taking location permissions. Also I have to track location permissions always like if user has given permission access after install the application and then after sometime user goes to the app settings in device settings and disable/revoked the permissions. Again once app comes from background to foreground, I have to check permission based on that, Needs to show the messages.
So that, I am using Appstate. But, In Android strangely, After installed the application, If user denied the permission with "Dont show again" checkbox, Then Appstate getting keep on changing with background and active always.
It is keep on loop.
componentDidMount = async () => {
AppState.addEventListener('change', this.handleAppStateChange);
};
componentWillUnmount() {
AppState.removeEventListener('change', this.handleAppStateChange);
Geolocation.clearWatch(this.watchID);
}
handleAppStateChange = async nextAppState => {
const {appState} = this.state;
console.log('nextAppState -->', nextAppState);
console.log('appState -->', appState);
if (appState === 'active') {
// do this
this.showLoader();
await this.requestAndroidLocationPermission();
} else if (appState === 'background') {
// do that
} else if (appState === 'inactive') {
// do that other thing
}
this.setState({appState: nextAppState});
};
requestAndroidLocationPermission = async () => {
try {
const granted = await PermissionsAndroid.request(
PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
{},
);
if (granted === PermissionsAndroid.RESULTS.GRANTED) {
this.getLatitudeLongitude();
} else if (granted === PermissionsAndroid.RESULTS.NEVER_ASK_AGAIN) {
this.hideLoader();
this.setState({
errorMessage: 'Location permission is denied',
isLoading: false,
});
} else {
this.hideLoader();
this.requestAndroidLocationPermission();
}
} catch (err) {
console.warn(err);
}
};
It is keep on printing (loop) after denied permission with Don't show again
appState --> active
nextAppState --> background
appState --> active
nextAppState --> background
appState --> active
nextAppState --> background
appState --> active
It goes on and never stop.
How to handle this? Any suggestions?
I had the same problem. Do not use AppState. Is faulty.
the problem lies within RN's definition of "background". react-native uses android's activity (the holder of the UI thread and where your UI lives) onPause callback as the trigger for sending the "background" signal. But, onPause is called everytime SOMETHING comes in front of your activity's view hierachy, like Dialogs (like the permission box), other activities (like a file picker), etc; for android react-native, "background" means "shadowed by a foreign UI element/android task" rather than "paused and sent to background to do something else", thus causing the loops you see. The shortest solution is to override onPause in your ReactActivity, and add control conditions to make sure super.onPause is only called when you are actually going to background, like checking your task stack, or if the permission dialog is being called, so you avoid this kind of loop/faulty call. A second option would be to provide your own app lifecycle event instead, with clear triggering conditions.
today I had a similar problem.
I could solve it using "focus" in android and "change" in ios.
I have a custom hook like this:
import { useEffect } from 'react';
import { AppState, Platform } from 'react-native';
const focusEvent = Platform.OS === 'ios' ? 'focus' : 'change';
const useLocationListener = () => {
useEffect(() => {
AppState.addEventListener(focusEvent, handleAppStateChange);
getLocationAsync();
return () => {
AppState.removeEventListener(focusEvent, handleAppStateChange);
};
}, []);
const handleAppStateChange = (nextAppState: string) => {
if (nextAppState === 'active') {
getLocationAsync();
}
};
const getLocationAsync = async () => {
const { canAskAgain, status } = await Permissions.getAsync(
Permissions.LOCATION
);
if (canAskAgain) {
const response = await Permissions.askAsync(Permissions.LOCATION);
// handle location
}
// handle location with "status"
};
};
export default useLocationListener;
You can use a flag that check whether app should handle background or it's just a permission call.
const shouldHandleBackground = useRef(true)
const handler = (state) => {
if (state === 'active' && shouldHandleBackground.current) {
doStuff()
}
}
// when calling for permisson make the flag false
shouldHandleBackground.current = false
await Android.permission.LocationPermission()
shouldHandleBackground.current = true
and after permission request you can make flag true

Linking.getInitialURL() is not being cleared after used for deeplink

I've had this problem for like 2 weeks. I used Wix's Navigation for navigating around the app. I followed this tutorial for implementing the deeplink/universal link.
I have a base class called BaseScreen where I keep all the deeplink handler like in the tutorial. This BaseScreen would looks like this:
componentDidMount(){
// this handles the case where the app is closed and is launched via Universal Linking.
Linking.getInitialURL()
.then((url) => {
if (url) {
// Alert.alert('GET INIT URL','initial url ' + url)
this.resetStackToProperRoute(url)
}
})
.catch((e) => {})
// This listener handles the case where the app is woken up from the Universal or Deep Linking
Linking.addEventListener('url', this.appWokeUp);
}
componentWillUnmount(){
// Remove the listener
Linking.removeEventListener('url', this.appWokeUp);
}
appWokeUp = (event) => {
// this handles the use case where the app is running in the background and is activated by the listener...
// Alert.alert('Linking Listener','url ' + event.url)
this.resetStackToProperRoute(event.url)
}
resetStackToProperRoute = (url) => {
// grab the trailing portion of the url so we can use that data to fetch proper information from the server
let trailing = url.slice(url.lastIndexOf('=') + 1, url.length)
// go to the desired screen with the trailing token grabbed from the url
this.props.navigator.resetTo({
screen: 'NewPassword',
overrideBackPress: true,
passProps: {
token: trailing
},
animated: true,
animationType: 'fade',
navigatorStyle: {
navBarHidden: true,
}
})
}
When the app launch, it'll show the screen LoginScreen which extends the BaseScreen above. After killing the app, click the url from the mail, the app launches LoginScreen first, then it'll redirect to the screen NewPassword, and after everything has done, I'll redirect back to LoginScreen by:
this.props.navigator.resetTo({
screen: 'LoginScreen',
animated: true,
overrideBackPress: true,
animationType: 'fade',
navigatorStyle: {
navBarHidden: true,
}
})
But the Linking.getInitialURL() of the LoginScreen still receive the old url, so it'll redirect to NewPassword again, and it's a loop.
I've also tried to pass: passProps: {} option when resetTo the LoginScreen but no luck.
I guess the only way to fix it is to clear the initialUrl manually after everything's done in NewPassword screen. The listener for the BaseScreen should be there because if I don't kill the app (just minimize it), the listener should be running to navigate to NewPassword.
Wix's navigation has a doc for Deeplink, I tried putting method onNavigatorEvent(event) into the BaseScreen but it doesn't get called. I don't know if I miss something.
Thank you for your time. Any idea would be appreciated
Linking.getInitialURL() gives us the same Url when we come back to the same page again, to Overcome this we can do a simple condition of not to call the DeepLink function. Something like...
Step 1: First init a dummyDeepLinkedUrl String .
var dummyDeepLinkedUrl;
Step 2: Check for the condition like, if deeplinkUrl is coming from Linking.getInitialURL() and deeplinkUrl is not equal to the dummyDeepLinkedUrl .
if (url && url != dummyDeepLinkedUrl) {}
Step 3: If not same call the Deeplink Function and assign the deeplinkUrl to dummyDeepLinkedUrl.
this.navigateToRespectivePage(url);
dummyDeepLinkedUrl = url;
Finally this will look like :
Linking.getInitialURL().then(url => {
if (url && url != dummyDeepLinkedUrl) {
this.navigateToRespectivePage(url);
dummyDeepLinkedUrl = url;
}
});
There are two ways to handle URLs that open your app.
If the app is already open, the app is foregrounded and a Linking event is fired You can handle these events with
Linking.addEventListener(url, callback).
If the app is not already open, it is opened and the url is passed in as the initialURL You can handle these events with
Linking.getInitialURL(url) -- it returns a Promise that resolves to
the url, if there is one.
You can read more detail
Here is the example
export default class App extends Component {
constructor(props) {
super(props)
this.state = {
initialised: false
}
}
componentDidMount() {
AppState.addEventListener('change', this._handleAppStateChange);
Linking.addEventListener('url', event => {
console.log('deep link from background', event.url)
})
}
_handleAppStateChange = async (nextAppState) => {
const url = await Linking.getInitialURL();
if (url !== null && !this.state.initialised) {
this.setState({ initialised: true })
console.log('deep link from init app', url)
}
}
componentWillUnmount() {
AppState.removeEventListener('change', this._handleAppStateChange);
Linking.removeEventListener('url')
}
}

Xamarin - Swipe to refresh the refresh icon still there after execute command

I am using xamarin forms to develop my project, and now I am using the Jason Smith Components to refresh my view.
Xamarin.Forms-PullToRefreshLayout
I am using the following code:
scrollControl.RefreshCommand = RefreshCommand;
public ICommand RefreshCommand
{
get {return new Command(async () => await ContentScrollView_Scrolled()); }
}
async Task ContentScrollView_Scrolled()
{
......
}
After I execute the code, the refresh icon still keep spinning. I tried to put the scrollControl.isRefreshing = false at the end. It does not work.
I suspect that when your refresh completes that you are not updating the IsRefreshing property on the UI thread. Try this:
// after your refresh command completes
Device.BeginInvokeOnMainThread (() => {
scrollControl.IsRefreshing = false;
});
Ok well, this solution is kinda weird, and i found it, since the isrefreshing property is false, so i set the isrefreshing to true at the beginning of code, i thought it will auto set to true so i have to manual it myself, in the end it is work like charm
scrollControl.RefreshCommand = RefreshCommand;
public ICommand RefreshCommand
{
get {return new Command(async () => await ContentScrollView_Scrolled()); }
}
async Task ContentScrollView_Scrolled()
{
scrollControl.isRefreshing = true;
......
scrollControl.isRefreshing = false;
}

Categories

Resources