skip to Main Content

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


  1. You’re trying to access stockData immediately after setting symbol state, but stockData state isn’t updated synchronously. useEffect hook has an asynchronous behaviour, so stockData may not have been updated by the time you try to access it in the handleSubmit function.

    Additionally, it’s important to remove stockData from the useEffect dependencies to prevent potential infinite loops caused by updating stockData inside the useEffect callback.

    If you want to print actual information of stockData in the console, you can use useEffect hook:

    useEffect(() => {
      console.log(stockData);
    }, [stockData]);
    
    Login or Signup to reply.
  2. 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 a useEffect is mostly correct, but you have set up that hook’s dependencies incorrectly. It should depend on setStockData but not on stockData.

    The way you have it currently, your useEffect will trigger itself each time it gets a response, because it updates stockData 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 the preventDefault 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 call setStockData, or set up another useEffect hook that depends on stockData so it will run each time you update it, and do whatever you need to with that data in there.

    For example:

    "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)"]);
    
            // Option A: use your data object here
          });
      }, [setStockData, symbol]);
    
      useEffect(() => {
        if (!stockData) {
          return;
        }
    
        // Option B: use stockData here
      }, [stockData]);
    
      return (
        <form
          onSubmit={handleSubmit(({ symbol }) => {
            setSymbol(symbol);
          })}
        >
          <label>symbol</label>
          <input {...register("symbol")} />
          <input type="submit" />
        </form>
      );
    };
    
    export default Home;
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search