I am trying to make an API call in my parent component and pass that data to the child component but I think the child component is getting rendered before the API call is complete and I'm receiving undefined
data in my child component.
Specifically, the error is:
Possible unhandled promise rejection (id: 0): TypeError: Cannot convert undefined value to object
I want to wait for the API call to finish then use that data to pass it on to my child.
Here's the parent:
const Parent = () => {
...
const [intraday, setIntraday] = useState<IntradayInterface>();
const [daily, setDaily] = useState<DailyInterface>();
const [weekly, setWeekly] = useState<WeeklyInterface>();
const [monthly, setMonthly] = useState<MonthlyInterface>();
useEffect(() => {
async function UpdateDetails()
{
const intradayJSON = await getIntradayData(newSymbol);
const dailyJSON = await getDailyData(newSymbol);
const weeklyJSON = await getWeeklyData(newSymbol);
const monthlyJSON = await getMonthlyData(newSymbol);
setIntraday(parseIntradayData(intradayJSON));
setDaily(parseDailyData(dailyJSON));
setWeekly(parseWeeklyData(weeklyJSON));
setMonthly(parseMonthlyData(monthlyJSON));
setIsLoading(false);
}
UpdateStockDetails();
}, []);
if(isLoading){
return <ActivityIndicator size='large' />
}
else{
return (
<ScrollView style={{ backgroundColor: black}}>
<View style={{ flex: 1, justifyContent: 'space-between', padding: 10 }}>
<View>
<Graph symbol={newSymbol}
intraday={intraday!}
daily={daily!}
weekly={weekly!}
monthly={monthly!}/>
</View>
...
And the child is my Graph
component.
Is there anyway I can make the parent wait for the api call to finish so I can pass on the result data to the child Graph
component?
You can conditionally render the Graph
component when all the props it needs are available. You should also surround any asynchronous logic in a try/catch
in order to handle thrown exceptions and rejected Promises.
Example:
const Parent = () => {
...
const [intraday, setIntraday] = useState<IntradayInterface>();
const [daily, setDaily] = useState<DailyInterface>();
const [weekly, setWeekly] = useState<WeeklyInterface>();
const [monthly, setMonthly] = useState<MonthlyInterface>();
useEffect(() => {
async function updateDetails() {
try {
const [intradayJSON, dailyJSON, weeklyJSON, monthlyJSON] =
await Promise.all([
getIntradayData(newSymbol),
getDailyData(newSymbol),
getWeeklyData(newSymbol),
getMonthlyData(newSymbol)
]);
setIntraday(parseIntradayData(intradayJSON));
setDaily(parseDailyData(dailyJSON));
setWeekly(parseWeeklyData(weeklyJSON));
setMonthly(parseMonthlyData(monthlyJSON));
} catch(error) {
// catch and handle/ignore errors/rejections
} finally {
// Clear loading status regardless of success/failure
setIsLoading(false);
}
}
updateStockDetails();
}, []);
const canShowGraph = newSymbol && intraday && daily && weekly && monthly;
if (isLoading) {
return <ActivityIndicator size='large' />
} else {
return (
<ScrollView style={{ backgroundColor: black }}>
<View
style={{
flex: 1,
justifyContent: 'space-between',
padding: 10
}}
>
<View>
{canShowGraph && (
<Graph
symbol={newSymbol}
intraday={intraday}
daily={daily}
weekly={weekly}
monthly={monthly}
/>
)}
</View>
...
</View>
...
</ScrollView>
);
}
};
It may also be a good idea to render some fallback UI in case the loading does fail and there isn't graph data to pass as props.
Example:
<View>
{canShowGraph ? (
<Graph
symbol={newSymbol}
intraday={intraday}
daily={daily}
weekly={weekly}
monthly={monthly}
/>
) : <View>No data.</View>}
</View>