Expo React Native execute function when entering view - react-native

I'm trying to make a function execute when a view is in foreground, but just once not on each update of the component. If the user navigates to another view and goes back to the first view it should execute that function again, but just once. Is there a solution to this?
if using useEffect without second parameter it executes on each update, if I add [] as second parameter it only executes the first time the view is rendered but not when navigating back to it.
Any help appreciated!

if you are using react-navigation you can do this by listen on screen focus see here
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// The screen is focused
// Call any action
});
// Return the function to unsubscribe from the event so it gets removed on unmount
return unsubscribe;
}, [navigation]);

Related

Trigger UseEffect whenever i switch tabs in React Native Tab Navigator

I have implemented react native tab navigator and added 4 screens to it.
I post some record to api in the second screen and i want to have the updated record in the 4th screens where i am getting updated records..
Useeffect only gets targeted only once, and when i put something in it's argument it gives me strange behavior.
I want useeffect to reload and call the api to get latest items in the 4th screen without putting anything in it's arguement(empty argument)
Any help would be highly appreciated.
Try doing this ;
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
makeApiCall();
});
return unsubscribe;
}, [navigation]);
Get navigation in component's arguments(destructuring)
like below;
const My4thTab = ({ navigation }) => {
}
This way useEffect will trigger only once, every time you come on this screen
but make sure to clear the previous state where you store your data, otherwise, there could be a record duplication.
Hope it helps :)

How unmount a hook after going to new screen with navigate

The context is a simple React Native app with React Navigation.
There are 3 screens.
The first simply displays a button to go to second screen using navigation.navigate("SecondScreen").
The Second contains a hook (see code below) that adds a listener to listen the mouse position. This hook adds the listener in a useEffect hook and removes the listener in the useEffect cleanup function. I just added a console.log in the listener function to see when the function is triggered.
This screen contains also a button to navigate to the Third screen, that only shows a text.
If I go from first screen to second screen: listener in hook start running. Good.
If I go back to the first screen using default react navigation 's back button in header. the listener stops. Good.
If I go again to second screen, then listener runs again. Good.
But if I now go from second screen to third screen, the listener is still running. Not Good.
How can I unmount the hook when going to third screen, and mount it again when going back to second screen?
Please read the following before answering :
I know that:
this is due to the fact that react navigation kills second screen when we go back to first screen, and then trigger the cleanup function returned by the useEffect in the hook. And that it doesn't kill second screen when we navigate to third screen, and then doesn't trigger the cleanup function.
the react navigation's hook useFocusEffect could be used to resolve this kind of problem. But it can't be used here because it will involve to replace the useEffect in the hook by the useFocusEffect. And I want my hook to be usable in every context, even if react navigation is not installed. More, I'm using here a custom hook for explanation, but it's the same problem for any hook (for example, the native useWindowDimensions).
Then does anyone know how I could manage this case to avoid to have the listener running on third screen ?
This is the code of the hook sample, that I take from https://github.com/rehooks/window-mouse-position/blob/master/index.js, but any hook could be used.
"use strict";
let { useState, useEffect } = require("react");
function useWindowMousePosition() {
let [WindowMousePosition, setWindowMousePosition] = useState({
x: null,
y: null
});
function handleMouseMove(e) {
console.log("handleMouseMove");
setWindowMousePosition({
x: e.pageX,
y: e.pageY
});
}
useEffect(() => {
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, []);
return WindowMousePosition;
}
module.exports = useWindowMousePosition;
the react navigation's hook useFocusEffect could be used to resolve this kind of problem. But it can't be used here because it will involve to replace the useEffect in the hook by the useFocusEffect. And I want my hook to be usable in every context, even if react navigation is not installed
So your hook somehow needs to know about the navigation state. If you can't use useFocusEffect, you'll need to pass the information about whether the screen is focused or not (e.g. with an enabled prop).
function useWindowMousePosition({ enabled = true } = {}) {
let [WindowMousePosition, setWindowMousePosition] = useState({
x: null,
y: null
});
useEffect(() => {
if (!enabled) {
return;
}
function handleMouseMove(e) {
console.log("handleMouseMove");
setWindowMousePosition({
x: e.pageX,
y: e.pageY
});
}
window.addEventListener("mousemove", handleMouseMove);
return () => {
window.removeEventListener("mousemove", handleMouseMove);
};
}, [enabled]);
return WindowMousePosition;
}
And then pass enabled based on screen focus:
const isFocused = useIsFocused();
const windowMousePosition = useWindowMousePosition({ enabled: isFocused });
Note that this approach will need the screen to re-render when it's blurred/focused unlike useFocusEffect.

Render useEffect/Async function from a difference screen

I have an async function and a useEffect that fetches data once.
const [data, setData] = useState([]);
async function fetchData() {
fetch(`${baseURL}api/v1/data/${userId}`)
.then((response) => response.json())
.then((response) => {
try {
if (response.length > 0) {
setData(response);
} else {
setData([]);
// console.log(response);
}
} catch (err) {
console.log('no response');
alert(err);
}
});
}
useEffect(() => {
fetchData();
}, [userId, data]);
I could remove the array on the use effect but it will always run the function if I do that.
So when I open the screen, it will fetch the latest data. However, if I want to add a new data from a different screen, it wont trigger the async nor the useEffect function. How should I tell RN that there is a new data? Would AsyncStorage work? to update a data from one screen and apply the data here? I am open for suggestions on how to proceed.
What I meant by a different screen: A register screen and a view screen. In this case, I already opened the View Screen before I open the register screen so view screen is already rendered.
In React Navigation and most of the navigation libraries, screens don't get unmounted from the stack when it's navigated to another screen. For example if you have a list of something and then you press to "+" button to navigate to the "new item" screen to add a new one, when you press back button, since the previous "list" screen was not unmounted from the stack, useEffect won't be triggered, and you won't get the new data.
There are a couple of solutions for this case:
You can hold your data in a global state, and when you update an item from another screen, after a successful API call, you can also update the global state. You can look for React Context, MobX or Redux for this.
You can pass parent's state with a callback from one screen to another if they are not that apart from each other. So that in the "new data" screen, you can call that callback function to change the parent screen's state too.
Third, and IMO the best way is using a hook called useFocusEffect by React Navigation itself: https://reactnavigation.org/docs/use-focus-effect
I hope these will help.

willFocus event in react navigation 5

I need to fetch data before focus event. I saw that there was an willFocus event in react navigation 4 but it seems that the event was removed in react navigation 5.
componentDidMount cannot do the trick because I want to fetch data as soon as possible even before the screen comes into focus each time the user navigate to my screen.
You could do something like:
/**
* Your redux action should set loading-state to false after
* fetch-operation is done...
*/
reduxActionDoingFetch();
useEffect(() => {
if (reduxActionDoingFetch_Loading === false) {
Navigation.navigate('YourTargetScreen');
}
}, [reduxActionDoingFetch_Loading]);
You CANNOT fetch data before componentDidMount. That's literally the first thing happens when a new screen is rendered.
Regarding fetching data each time when the screen is focused, you need to use focus event as mentioned in the migration guide: https://reactnavigation.org/docs/upgrading-from-4.x/#navigation-events
The focus event equivalent to willFocus from before. It fires as soon as the screen is focused, before the animation finishes. I'm not sure what you mean by it fires too late.
Also, for data fetching, there is a special hook: useFocusEffect
https://reactnavigation.org/docs/use-focus-effect/
React.useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
// do something
});
return unsubscribe;
}, [navigation]);

how can i send callback to my parent view when call Actions.pop in react-native-router-flux

I am using https://github.com/aksonov/react-native-router-flux for navigation in react native.How can I send callback to my parent view when I call pop actions on it.
onPressed() {
Actions.pop();
}
This is how I call pop action on it but I need to send updated value to previous view.
will I finally solve it by calling empty refresh with props after delay
Actions.popTo('pageOne');
setTimeout(() => {
Actions.refresh({name:'zzzzar'});
console.log("zzzz");
}, 10);