Reputation: 265
I'm getting an error when my chart component renders due to its initial state which is an empty array before the API response. It would say something like Cannot read property '0' of undefined
which is due to the fact that when the component loads, its initial state is, well... an empty array. So I'd like to know if there's a way to make the component render only after the API response is received.
Here is the parent component:
function InformationPage({
match: {
params: { symbol },
},
}) {
const [chartData, setChartData] = useState([]); {/*Declaring state*/}
useEffect(() => { {/*Fetching API response here to set state*/}
axios
.get(
`https://finnhub.io/api/v1/stock/candle?symbol=${symbol}&resolution=15&from=1572651390&to=1602623478&token=xxxxxxxxx`
)
.then((res) => {
console.log(res.data);
setChartData(res.data);
})
.catch((err) => {
console.log(err);
});
}, [symbol]);
return (
<Grid item container xs={12} sm={12} md={6} lg={8}>
{chartData && <QuoteChart chartData={chartData} iex={iex} />}
</Grid>
);
}
export Default InformationPage;
Here is the child chart component, basically when I do the fetch request from axios it returns an array that will get mapped in the chartRender
object. I just need to find a way to render the component only AFTER the api request from axios is finished as to not have an error. Any help would be greatly appreciated.
function QuoteChart(props) {
const classes = useStyles();
const { chartData } = props;
const chartRender = chartData.map((chartConfig) => ({
x: new Date(chartConfig?.t),
y: [chartConfig?.o, chartConfig?.h, chartConfig?.l, chartConfig?.c],
}));
// const chartDataLog = console.log(chartData);
const config = {
series: [
{
data: [{ chartRender }],
},
],
Upvotes: 0
Views: 7675
Reputation: 2146
Update: According to Finnhub API it's an object that you receive from the API, not an array. This means that charData.length
in my previous answer will never be evaluated to true
and the child component QuoteChart
will never get rendered.
I'd suggest the following changes. In your parent component, make charData = null
initially:
const [chartData, setChartData] = useState(null);
When the API has returned a response and chartData
has been assigned a value your child component can be rendered:
{chartData && <QuoteChart chartData={chartData} iex={iex} />}
The chart you want to render in the child component expects the following input: [{ x: date, y: [O,H,L,C] }]
. Because map()
can only be called on arrays and chartData
is not one, you should navigate to one of the arrays inside the said object, e.g. chartData.t
:
const chartRender = chartData.t.map((timestamp, index) => ({
x: new Date(timestamp),
y: [chartData.o[index], chartData.h[index], chartData.l[index], chartData.c[index]],
}));
Finally, series.data
expects an array, you don't need to additionally wrap it in an object. So this should work:
const config = {
series: [{
data: chartRender
}]
};
You initialize chartData
with an empty array, therefore your condition should look like this:
{chartData.length && <QuoteChart chartData={chartData} iex={iex} />}
If you expect the API to return an empty list, I'd recommend to set an undefined
or null
as the initial value of chartData
:
const [chartData, setChartData] = useState(null);
In this case you can keep your condition as is.
Upvotes: 8
Reputation: 367
The easiest way would require a simple small change to your code:
{chartData.length && <QuoteChart chartData={chartData} iex={iex} />}
After researching finnhub - I see that the response is not an array. I tested this url:
https://finnhub.io/api/v1/stock/candle?symbol=AAPL&resolution=15&from=1572651390&to=1602623478&token=
The response is an object with a "c" property. Your code should be modified accordingly. For the above link example:
setChartData(res.data.c);
Upvotes: 0
Reputation: 14211
You need to wrap your axios call in an async function and wait
there
useEffect(() => { {/*Fetching API response here to set state*/}
const getInfo = async () => {
await res = axios
.get(
`https://finnhub.io/api/v1/stock/candle?symbol=${symbol}&resolution=15&from=1572651390&to=1602623478&token=xxxxxxxxx`
)
console.log(res.data);
setChartData(res.data);
}
getInfo()
}, [symbol]);
Upvotes: 0
Reputation: 45
If I'm not mistaken you can simply use async/await for your problem. So change the useEffect line to
useEffect(async () => { {/*Fetching API response here to set state*/}
and then
await axios
.get(
`https://finnhub.io/api/v1/stock/candle?symbol=${symbol}&resolution=15&from=1572651390&to=1602623478&token=xxxxxxxxx`
)
Then your application should wait for the axios to finish fetching
Upvotes: 1