I tried a bunch of different variations of useCallback and useMemo, as well as React.memo, but it doesn't have the behavior I want, can anyone help me? The problem is that whenever the state of the parent is changed (like when I turn the phone side ways, or when "isShown" changes) it re-renders the child and re-triggers the read-data function which not only waste data because it causes another call to the backEnd, but the user also has to wait for the data to be fetched again. Is there a way to not re-trigger handleReadData of the child each time the parent is re-rendered?
parent :
export default function Parent({}) {
const [isShown, setIsShown] = React.useState(false);
{...}
const WifiModuleSection = ({title, wifiModule, wifiSection}) => {
return (
<View style={styles.sectionWifiModuleContainer}>
<View>
{...}
</View>
<WifiModuleControls wifiModule={wifiModule} t={t} i18n={i18n} />
</View>
);
};
return return (
<View>
{WifiModules.map(wifiModule => {
return (
<View style={{display:isShown ? 'flex' : 'none'}}>
<WifiModuleSection
title={wifiModule.name}
wifiModule={wifiModule}
wifiSection={wifiSection}
/>
</View>
);
})}
{...}
</View>
);
}
child :
const WifiModuleControls = ({wifiModule, t, i18n}) => {
{...}
const handleReadData = async () => {
//Request Read state-data
try {
setLoadingReadData(true);
const formData = {...};
const res = await client.post('/read-data', formData);
if (res.data.success) {
{...}
setData(res.data.data);
setLoadingReadData(false);
} else {
Toast.showShortBottom('Error: ' + res.data.message);
setLoadingReadData(false);
}
} catch (error) {
Toast.showShortBottom('Error : ' + error);
setLoadingReadData(false);
}
};
useEffect(() => {
handleReadData();
}, []);
if (loadingReadData) {
return (
<ActivityIndicator
style={{marginBottom: 1}}
color={currentMainThemeColor}
size={60}
/>
);
}
return (
<View>
{...}
</View>
);
};
export default WifiModuleControls;
I simplified code to only show relevent parts
This isn't about preventing re-renders. It's either about:
The problem is WifiModuleControls
owns the fetching of data and the holding of it in state so that when it unmounts, that data is then "lost" and the only way to get it back is to refetch it when your component mounts again.
This happens because of this line:
{isShown &&
WifiModules.map(wifiModule => {
return (
<WifiModuleSection
title={wifiModule.name}
wifiModule={wifiModule}
wifiSection={wifiSection}
/>
);
})}
If isShown
is false, then the components unmount and the data is gone. When isShown
is true again, the components mount again and the only way to get their data back to refetch it all again.
You can do several things to resolve your issue:
WifiModuleSection
. This way, when WifiModuleSection
unmounts, the data is still held in the parent component.EDIT:
Option 1: I'm relatively naive to react-native, but if you wanted to keep everything else the same, instead of conditionally rendering based on isShown
, the below would probably work:
{WifiModules.map(wifiModule => {
return (
<View style={{display:isShown ? 'flex' : 'none'}}>
<WifiModuleSection
title={wifiModule.name}
wifiModule={wifiModule}
wifiSection={wifiSection}
/>
</View>
);
})}
This means that you'll have to get rid of the conditional rendering using isShown
- so the whole snippet looks like this:
{isShown && // remove this line entirely
WifiModules.map(wifiModule => {
return (
<View style={{display:isShown ? 'flex' : 'none'}}> // replace with this conditional
<WifiModuleSection
title={wifiModule.name}
wifiModule={wifiModule}
wifiSection={wifiSection}
/>
</View>
);
})}