I'm getting an error when my chart ponent 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 ponent loads, its initial state is, well... an empty array. So I'd like to know if there's a way to make the ponent render only after the API response is received.
Here is the parent ponent:
function InformationPage({
match: {
params: { symbol },
},
}) {
const [chartData, setChartData] = useState([]); {/*Declaring state*/}
useEffect(() => { {/*Fetching API response here to set state*/}
axios
.get(
`=${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 ponent, 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 ponent 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 }],
},
],
I'm getting an error when my chart ponent 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 ponent loads, its initial state is, well... an empty array. So I'd like to know if there's a way to make the ponent render only after the API response is received.
Here is the parent ponent:
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 ponent, 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 ponent 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 }],
},
],
Share
Improve this question
asked Oct 15, 2020 at 12:53
PacholoamitPacholoamit
2656 silver badges18 bronze badges
4 Answers
Reset to default 8Update: 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 ponent QuoteChart
will never get rendered.
I'd suggest the following changes. In your parent ponent, 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 ponent can be rendered:
{chartData && <QuoteChart chartData={chartData} iex={iex} />}
The chart you want to render in the child ponent 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 remend 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.
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
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]);
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);