0

I am still fairly new to working with async and await functions, and even newer to implementing promise functions. I am trying to print out whatever my asynchronous function returns. When I log the output to the console, it always shows me the correct output, but when I try to save it and return it, it only works every other go. I have this similar code in react and it seems to work fine on the first go there, but with NextJs I am running into the issue that it does not run on the first go but second. I am attaching my code and would appreciate a general direction for where I can go to solve this issue.

import React, {useState} from 'react'
import { getStockPrice } from '../configs/stockapi';

function HomeInvestment() {
  const [entered, setEntered] = useState(false);
  const [ticker, setTicker] = useState('');

  
  const [price, setPrice] = useState(0);
  const [checks, setChecks] = useState([])

  const clear = () => {
    setEntered(false);
    setChecks([])
  }

  const handleSubmit = async() => {
    const idT = document.getElementById('t');
    console.log(ticker);  
    const p = stockPrice(ticker).then(result => {

      let data = {
        ticker: ticker,
        price: result
      }
      setChecks([...checks, data])
      setPrice(p);
    })
    setEntered(true);
    console.log(checks);
    idT.value = null
  }

  const stockPrice = async(ticker) => {
    return new Promise((resolve, reject) => {
      getStockPrice(ticker)
      .then(result => {
        console.log(result)
        resolve(result.toFixed(2))
      })
      .catch(error => {
        reject(error)
      })
    })
    
  }

  return (
    <div className='flex justify-center'>
        <div className='w-full max-w-[40ch] bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4'>
          <div className='flex text-black justify-center flex-col bg-slate-300 px-2 py-3 rounded mb-6'>
              <div className='mb-4'>
                <label className="block text-gray-700 text-sm font-bold mb-2 text-center" >
                    Enter Stock Ticker
                </label>
                <input id= 't' required className="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline"  type="text" placeholder="Stock Ticker" onChange={(e) => setTicker(e.target.value)}/>
              
              </div>
              <button className='bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full focus:outline-none focus:shadow-outline mx-10 ' type="button" onClick={handleSubmit}>
                Check Stock Info
              </button>
          </div>

          {entered && 
            <div>
              <div className=''>
                <button onClick={clear} className="text-red-600">Clear</button>  
              </div>
              <div className='flex text-black justify-center flex-col bg-slate-300 px-2 py-3 rounded'>
                  {checks.forEach((element) => {
                    return (
                      <div>
                        <p>{element.ticker}</p>
                        <p>{element.price}</p>
                      </div>
                    )
                  })}
                  {price}
              </div>
            </div>
          }

        </div>
    </div>
  )
}

export default HomeInvestment
9
  • 2
    Which exact part of your code is not working as you expect it to? What's the flow you're expecting? Commented Dec 12, 2022 at 21:30
  • It's a good idea when working with Promise stuff and the new async syntax to always use that, and not use the old .then() etc Promise API. That's what the async syntax features are meant to simplify. Commented Dec 12, 2022 at 21:31
  • It appears that the getStockPrice function is not returning a Promise, or is not being treated as one. Commented Dec 12, 2022 at 21:34
  • Why don't you use async with await? codesandbox.io/s/xenodochial-feather-t2heml?file=/src/… Can you replicate your code here? Commented Dec 12, 2022 at 21:34
  • const stockPrice = async (ticker) => { return getStockPrice(ticker) .then((result) => { console.log(result); }) .catch((error) => {}); }; Or const stockPrice = async (ticker) => { return await getStockPrice(ticker) }; Commented Dec 12, 2022 at 21:41

1 Answer 1

1

After removing all the noise coming from your specific use case, it seems like you might have a problem understanding the concept of a Promise. A Promise is an object that promises you that it will return a value at some point. You first need to wait for the promise to give you a value in order to manipulate it. The old way of handling Promises was with the function properties then and catch, but you should now be using the async and await keywords.

Reading the new implementation of handleSubmit, we are first waiting for getStockPrice to give a new price value before manipulating the value we're given. After the Promise has resolved, we can do whatever we want with the value. In your specific case, we're updating the state of the component.

import { useState } from 'react'
import { getStockPrice } from '../configs/stockapi'

const HomeInvestment = () => {
  const [entered, setEntered] = useState(false)
  const [ticker, setTicker] = useState('')

  const [price, setPrice] = useState(0)
  const [checks, setChecks] = useState([])

  const handleChange = (event) => {
    setTicker(event.target.value)
  }

  const handleSubmit = async () => {
    const newPrice = await getStockPrice(ticker)
    const formattedNewPrice = newPrice.toFixed(2)

    const check = {
      ticker,
      price: formattedNewPrice,
    }
    setChecks([...checks, check])
    setPrice(formattedNewPrice)
    setEntered(true)

    const idT = document.getElementById('t')
    idT.value = null
  }

  return (
    <div>
      <div>
        <div>
          <label>Enter Stock Ticker</label>
          <input id="t" required type="text" placeholder="Stock Ticker" onChange={handleChange} />
        </div>
        <button type="button" onClick={handleSubmit}>
          Check Stock Info
        </button>
      </div>

      {entered && (
        <div>
          {checks.map((check) => (
            <div>
              <p>{check.ticker}</p>
              <p>{check.price}</p>
            </div>
          ))}
          {price}
        </div>
      )}
    </div>
  )
}

export default HomeInvestment

One other thing to note is the fact that you were using Array.forEach instead of Array.map inside of your return statement. Using the second one will actually display some data for each of the array's entry on screen.

I haven't actually tested this component, but this should get you started. Watch out for the DOM manipulation you're doing with document.getElementById; you should probably look into useRef.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you so much for the help, this works for the most part. When I originally tried to implement it, it was only returning 0's, but after I added a console.log statement printing out the price, it became the correct price. How do I avoid this if I were to want to remove the console.log
another issue that I am running into is when I am using a stock that I havent used before, it prints out the correct price but does not save it, and instead submits a 0. how do i avoid that
"but you should now be using the async and await keywords." I disagree on this. There are some circumstances in which promise chaining is more convenient for the job and vice versa.
@Dotman If this solved your issue, I think you could mark my answer as the accepted one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.