I am developing react-native project. This question is mainly about the useFocusEffect hook behaviour in my project.
I have several screens. There is a share data & each screen can update the data. So, I decided to use context
to host the data via useState
hook. When one screen setMyData(
), the up-to-date data is available to all screens.
My data context to host shared data via useState
hook:
import React, {useState} from 'react';
const MyDataContext = React.createContext();
export const MyDataProvider = ({children}) => {
const [myData, setMyData] = useState([]);
...
return (
<MyDataContext.Provider
value={{
myData,
setMyData,
}}>
{children}
</MyDataContext.Provider>
);
}
export default MyDataContext;
(My App
component is wrapped by the MyDataContext.Provider
, I just don't show that part here since it is not the point of my question)
In each of my screen components, I need to process my data once when the screen is on the foreground (visible), and update the global state with updated data. I use useFocusEffect hook. Something like below:
import {useFocusEffect} from '@react-navigation/native';
import MyDataContext from '../context/MyDataContext';
const MyScreenOne = ({navigation})=> {
const {myData, setMyData} = useContext(MyDataContext);
useFocusEffect(() => {
console.log('################# Screen one is FOCUSED! ');
const updatedData = processData([...myData]);
// set my data
setMyData(updatedData);
return () => {
console.log('Screen one was unfocused');
};
}, []);//empty array to force code in hook only run once
return (<View>
<MyButton onPress={()=> navigation.navigate("MyScreenTwo")}>
...
</View>
)
}
...
As you can see I have a button which navigates user to another screen. The other screen has the same structure of having that useFocusEffect
hook in which it processes & updates data of global state.
When I run my app, the following happens to me:
At first MyScreenOne
is launched. useFocusEffect
is invoked, data is processed and updated and set to global state via the setMyData()
Since setMyData()
is called in above step, the re-rendering happens, at this time, the useFocusEffect
of MyScreenOne
is not invoked again, which is good and expected.
Now I press the button which navigates to MyScreenTwo
. Same process happens on MyScreenTwo
: useFocusEffect
is invoked, data is processed and updated and set to global state via the setMyData()
. NOTICE: MyScreenOne
is now not visible and in background stack/memory.
Since in above step the global state is updated by the second screen, re-rendering happens again to all screens in memory. This time surprisingly the useFocusEffect
of MyScreenOne
is invoked again, because of that the global state is updated again by the hook in MyScreenOne
again, and re-rendering happens again to screens in memory.
As you can imaging , endless re-rendering happens now due to in above step the background screen MyScreenOne
's useFocusEffect
is invoked surprisingly.
Isn't useFocusEffect
supposed to be run only when screen is visible? Why the useFocusEffect
hook of background screen is also invoked when re-rendering happens? How to achieve what I need to only run the process data & update data once on each screen when the screen is visible in the meantime the data can be shared by all screens?
useFocusEffect
doesn't take a dependency array. You need to pass useCallback
as mentioned in the docs:
useFocusEffect(
React.useCallback(() => {
// whatever
}, [])
);