I have a React Native app where I'm navigating to the StudioServices screen after adding a new service. However, when navigating back to the StudioServices screen, the data is not refetched. The issue arises because the useStudioServices hook fetches data only when the component initially mounts, and not when the screen gains focus after navigating back.
Here’s the setup:
StudioServices Screen - Displays a list of services. useStudioServices Hook - Fetches data from the API (GetStudioServices) and returns the services, loading, and error states. ServiceAdd Screen - Navigates to StudioServices after saving a new service.
const StudioServices = () => {
const { services, loading, error } = useStudioServices();
if (loading) {
return <LoadingScreen />;
}
if (error) {
return (
<View>
<Text>Error</Text>
</View>
);
}
return <ServiceList services={services} />;
};
useStudioServices Hook:
export const useStudioServices = () => {
const [services, setServices] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchData = async () => {
try {
setLoading(true);
const result = await GetStudioServices();
setServices(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
};
useEffect(() => {
fetchData();
}, []);
return { services, loading, error };
};
ServiceAdd Screen:
const ServiceAdd = () => {
const navigation = useNavigation();
const handleSave = async (newService) => {
await InsertService(newService);
navigation.navigate("StudioServices", { newService });
};
};
After navigating to the ServiceAdd screen and saving a new service, when I navigate back to the StudioServices screen using navigation.navigate("StudioServices"), the data is not refetched.
The useEffect in the useStudioServices hook only runs once when the component first mounts, but not when I navigate back.
You have to make some changes in your hook file see below code:
import { useState, useCallback } from 'react';
export const useStudioServices = () => {
const [services, setServices] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const fetchData = useCallback(async () => {
try {
setLoading(true);
const result = await GetStudioServices();
setServices(result);
} catch (error) {
setError(error);
} finally {
setLoading(false);
}
}, []); // useCallback ensures the function reference remains stable
useEffect(() => {
fetchData();
}, [fetchData]);
return { services, loading, error, fetchData };
};
Now you can call fetchData in screen when you come back to screen, there are two was for that, one is with navigation listner and other is with useFocusEffect and useCallback hooks.
With hooks:
import { useFocusEffect } from '@react-navigation/native';
const StudioServices = () => {
const { services, loading, error, fetchData } = useStudioServices();
// Refetch data whenever the screen comes into focus
useFocusEffect(
React.useCallback(() => {
fetchData();
}, []) // Add dependencies if needed
);
if (loading) {
return <LoadingScreen />;
}
if (error) {
return (
<View>
<Text>Error</Text>
</View>
);
}
return <ServiceList services={services} />;
};
With navigation listner:
import { useEffect } from 'react';
import { useNavigation } from '@react-navigation/native';
const StudioServices = () => {
const navigation = useNavigation();
const { services, loading, error, fetchData } = useStudioServices();
useEffect(() => {
const unsubscribe = navigation.addListener('focus', () => {
fetchData();
});
return unsubscribe; // Cleanup the listener when the component unmounts
}, [navigation, fetchData]);
if (loading) {
return <LoadingScreen />;
}
if (error) {
return (
<View>
<Text>Error</Text>
</View>
);
}
return <ServiceList services={services} />;
};