Reputation: 340
I am writing a React-Native app using Expo. When I try to render the below page on my devices, I get errors that memorialDetails.Name
is undefined. From what I can tell, useEffect is just not executing no matter what, which is causing the API calls to not trigger, hence the undefined data.
I have confirmed via console.log statements and from watching the backend logs that the API calls are not being triggered.
Here is an Expo Snack that you can look at that shows the issue. Line 30 on MemorialDetailScreen.js can be changed from a token to plain text to make the page render, and then as soon as it is changed back to a token, it crashes.
https://snack.expo.dev/@djfriar/memorialmanager
When I run this, I see the following in the console, so the useEffect is never getting called. It seems like the app is trying to render the entire screen before calling the API:
iOS Bundling complete 51ms
==== MemorialListScreen ====
==== memorial API ====
==== MemorialNavigator ====
iOS Running app on iPhone 12 mini
==== memorialID ====
9
===== memorialDetails =====
undefined
==== memorialID ====
9
===== memorialDetails =====
undefined
I have forced refresh, restarted the entire app / Expo, etc. It does this on both my iOS and Android dev devices.
Upvotes: 0
Views: 4796
Reputation: 202751
The useEffect
is guaranteed to be called at least once on the initial render cycle. You've a bug in the render return that prevents the hook from firing at the end of the render cycle.
On the initial render the useApi
hook is returning an object with a data
property that is an empty array.
export default useApi = (apiFunc) => {
const [data, setData] = useState([]);
const [error, setError] = useState(false);
const [loading, setLoading] = useState(false);
const request = async (...args) => {
console.log("==== useApi ====");
setLoading(true);
const response = await apiFunc(...args);
console.log(response.data);
setLoading(false);
if (!response.ok) return setError(true);
setError(false);
setData(response.data);
};
return { data, error, loading, request };
};
...
const getMemorialDetailsApi = useApi(memorial.getMemorialDetails);
On the next line you access the zeroth element
const memorialDetails = getMemorialDetailsApi.data[0];
which is obviously going to be undefined. This will throw an error during the initial render when attempting to access the property of undefined.
<AppText style={styles.memorialName}>
{memorialDetails.Name}
</AppText>
To guard against this you can use the Optional Chaining operator on ensure the data
array exists and provide a valid fallback value.
const memorialDetails = getMemorialDetailsApi.data?.[0] ?? {};
This allows there to at least always be a defined object to reference nested properties from later.
Upvotes: 2