Trigger an analytics event when user see specific screen - reactjs

DESCRIPTION
I have an app that should trigger an analytics event just once at a specific screen when a component is shown to the user.
i.e: if the user open the application and land to home screen and the component is showing, then the analytics event should be sent. If the user navigate to Settings screen and the component is there, then the event should trigger, but if the user navigate back to home screen, the event shouldn't trigger. Just have to be called once per screen.
If the user quit the app completely, the event should fire again once reopen it
STACK
react-native
react-navigation
react-redux
WHAT I'VE DONE
I've tried to use the useIsFocused hook with useEffectand useRef but it turns that when the screen is rerender for an async fetch event, the state inside my useEffect is reset and the analytics event trigger again, which is not the expected behavior.
const isHasBeenFocused = useRef()
const isFocused = useIsFocused()
useEffect(()=>{
if(isHasBeenFocused.current){
return;
}
isFocused && dispatch(analyticsEvent, payload)
isHasBeenFocused.current = isFocused
}, [])
This is my approach, but I don't know if it's the correct one or if there's another, better option.
Hope anyone could help me on this.

If you want the analytics event to fire only once for the lifetime of the app installation, you could use AsyncStorage for flags like this. AsyncStorage used to be a core RN module but is now community-based.
This will persist until the app is uninstalled.
useEffect(() => {
const check = async () => {
const hasReported = await AsyncStorage.getItem('yourKey');
if (hasReported) return;
AsyncStorage.setItem('yourKey', true);
dispatch(analyticsEvent, payload);
};
check();
}, []);
If you want the analytics to fire once per session, you could use the same solution with an AppState listener to reset the value to something falsy when the app is backgrounded. There's a good example of using AppState in the React Native docs.
If you need the value to persist across app installs, the best solution is to store it in your backend.

Related

react-query refetchOnWindowFocus in React Native not working

I am trying to setup react-query's App Focus Refetch in React Native per: https://tanstack.com/query/v4/docs/react-native#refetch-on-app-focus
I have a custom hook which is just a wrapper for the default app focus event listener. However, when leaving and entering the app my queries don't seem to refetch. Do I need to configure anything else? It doesn't say it in the docs.
const [appActive] = useAppState();
useEffect(() => {
focusManager.setFocused(appActive);
}, [appActive])
I don't know if you're using the useAppState hook from the #react-native-community/hooks package, but if you are, the hook doesn't return an array.
As you can see here, it returns the type of AppStateStatus.
If that is indeed the case, then you need to set the focused state like this:
const state = useAppState();
useEffect(() => {
focusManager.setFocused(state === 'active');
}, [state])

Preventing goBack with useNavigate hook

Is there a way to prevent back navigation using react's useNavigate hook? I'm currently using navigate("/",{replace: true}) for the time being but would like to have a proper solution for it.
The only way to fight browser's own mechanism like History and navigation is to check history.length:
useEffect(() => {
if (global.history.length> 1) {
window.open(window.location.href, "_blank");
}
}, []);
Imo it's rather confusing. Also when user opens new tab, history.length becomes 1. So code above will not bring another tab only if opened from bookmark/by clicking link with target="_blank".

React Native - Redux selector not triggering re-render on app resume

I have a fairly standard React Native functional component that uses a selector to retrieve a view model from my Redux store(Using redux-observable for async actions), and render it on changes, pseudo code below.
I'm implementing deeplinking, and my problem is that the store code works (I parse the URL and dispatch actions to my Redux store depending on route, navigate to the page, and the data should show) but it does not seem to re-render after an asynchronous action.
From what I can see, the action is dispatching in the background, it makes the async call, dispatches a success action and the store is updated successfully, the component simply does not re-render.
Any ideas? It seems to be an issue with the useSelector hook not firing, on that specific component that is loaded, as navigating away and using other pages works fine.
The user flow is:
User presses a notification which contains a deeplink
This navigates to a specific page
Data is loaded in the background
Upon successful fetch, the page should re-render
function MyPage() {
const dispatch = useDispatch();
const vm = useSelector(selectMyPageVM);
return (<View><Text>{vm.someProp}</Text></View>);
}

Prevent going back to previous screen with React Native

I'm working on an app with React Native and React Navigation. For most of the app, I have a bottom tab navigator and header. However, in one section of the app, the user can make a call and is taken to a screen with no navigation. The user shouldn't be able to navigate to the previous screen without ending the call. How can I prevent the user from going back?
if you use react-native-navigation v5, it supports several listeners and the one you need is beforeRemove this is being called before the screen gets removed and you can prevent it's default behaviour conditionally.
useEffect(() => {
navigation.addListener("beforeRemove", (e) => {
if(yourCondition) {
return ;
} else {
e.preventDefault();
}
});
}, [navigation]);

How to detect swipe back event in iphone React Native

I am developing a react native app, I am trying to clear setinterval function by backhandler event. It works correctly on android phone, but there is no back button in iphone, so I can't stop setinteval function. How can I detect swipe back handler?
Assuming that you are using #react-navigation/native. You can add Listener for beforeRemove event. This event is emitted every time the component is removed/user tries to leave the screen.
navigation.addListener('beforeRemove', (e) => {
e.preventDefault()
//clear setInterval here and go back
})
You can find more information here
For me, this snippet is working fine. you can place this code in useEffect or componentDidMount. Make sure to cancel this event if you are navigation between the screens.
For reference, you can visit https://reactnavigation.org/docs/5.x/navigation-events
and select the react-navigation version according to your project.
const gestureEndListener = () => {
console.log(" Called when screen is unmounted " )
}
useEffect(() => {
const gestureHandler = navigation.addListener('didBlur', gestureEndListener);
return () => {
gestureHandler.remove();
};
}, []);

Resources