I want to set stock data in stockData
and print it in console when I submit form.
but when I submit form, I got only undefined
.
I think it’s because it takes time fetch data from api(not sure).
How can I fix this code?
"use client";
import { useEffect, useState } from "react";
import { useForm } from "react-hook-form";
const MY_API_KEY = process.env.NEXT_PUBLIC_MY_API_KEY;
const Home = () => {
const [stockData, setStockData] = useState();
const [symbol, setSymbol] = useState();
const { register, handleSubmit } = useForm();
useEffect(() => {
if (!symbol) {
return;
}
const apiCall = `https://www.alphavantage.co/query?function=TIME_SERIES_DAILY_ADJUSTED&symbol=${symbol}&outputsize=compact&outputsize=full&apikey=${MY_API_KEY}`;
fetch(apiCall)
.then((response) => response.json())
.then((data) => {
setStockData(data["Time Series (Daily)"]);
});
}, [stockData, symbol]);
return (
<form
onSubmit={handleSubmit(({ symbol }) => {
setSymbol(symbol);
if (!stockData) {
return;
}
console.log(stockData);
})}
>
<label>symbol</label>
<input {...register("symbol")} />
<input type="submit" />
</form>
);
};
export default Home;
2
Answers
You’re trying to access
stockData
immediately after setting symbol state, butstockData
state isn’t updated synchronously.useEffect
hook has an asynchronous behaviour, sostockData
may not have been updated by the time you try to access it in thehandleSubmit
function.Additionally, it’s important to remove
stockData
from theuseEffect
dependencies to prevent potential infinite loops caused by updatingstockData
inside theuseEffect
callback.If you want to print actual information of stockData in the console, you can use
useEffect
hook:Fetching data is asynchronous, so you can’t handle the response synchronously in your submit handler like you’ve tried to now. Even if you were able to fetch that data synchronously, because of the way React’s
useState
hooks you still wouldn’t be able to access the new value via your state variable immediately anyway, since it won’t be set until the component is re-rendered.The way you are calling
setStockData
within auseEffect
is mostly correct, but you have set up that hook’s dependencies incorrectly. It should depend onsetStockData
but not onstockData
.The way you have it currently, your
useEffect
will trigger itself each time it gets a response, because it updatesstockData
and that triggers the hook to run again. Your tab won’t freeze up because it’s asynchronous, but it’s still not what you want to happen. If you watch the network tab in your browser’s dev tools, I’d expect you to see an unending series of requests to your stock data endpoint, with a new one fired immediately after the previous one comes back.If you need to access
stockData
directly in your submit handler… well the bad news is basically you can’t. You need to wait until the response has arrived to handle it, and because of the way event handlers work in JavaScript that’s not something you can do directly.Even if you did your
fetch
directly in the handler, handling the response asynchronously comes with some downsides which aren’t specific to React, for example it would be too late at that point to call thepreventDefault
method on the event object, if that’s something you needed to do based on the response you get.You need to either use your stock data response within your existing
useEffect
, where you currently callsetStockData
, or set up anotheruseEffect
hook that depends onstockData
so it will run each time you update it, and do whatever you need to with that data in there.For example: