1

I'm making a react app that works with a API that provides data to my App. In my data base I have data about pins on a map. I want to show the info of those pins on my react app, I want them to render. I get that information with axios and this url: http://warm-hamlet-63390.herokuapp.com/pin/list I want to retrieve the info from that url, with axios.get(url), stringify the JSON data and then parse it to an array of pins.

The Problem: My page will be rendered before I get the data back from the server, because axios is async, so I will not be able to show anything. UseEffect and useState won't work because I need something in the first place (I think).

What i've tried: I tried to use useEffect and useState, but as I said, I think I need something in the first place to change it after. I also tried to use await, but await won't stop the whole React App until it has a response, although it would be nice if there is something that stops the app and waits until I have the array with the info so I can show it on the App then. I tried everything with async. I'm fairly new to React so there might be something basic i'm mssing (?). I've been on this for days, I can't get this to work by any means.. Any help, youtube videos, documentation, examples, is help. Anything. How the hell do I render something that needs to wait for the server respond?

My code:

//function that stores the data in the result array, 
//but result array will only be available after the 
//server response, and after the page is rendered
async function pin(){

  const result = []
  var url = "http://warm-hamlet-63390.herokuapp.com/pin/list"

  const res = await axios.get(url)
  
  console.log(res.data.data);

  if(res.data){
    const txt = JSON.stringify(res.data.data)
    const result = JSON.parse(txt)
    console.log(result);
  }
  return result;
}
class App extends React.Component{

  render(){
    return(
      <div>
        <Pin/>
        <Mapa/>
      </div>
    )
  }
}


export default App
Enguias
  • 73
  • 2
  • 7

2 Answers2

4

I don't fully understand what you are trying to output but how you would usually handle this is with both the useState hook and the useEffect hook see example below.

  //function that stores the data in the result array, 
  //but result array will only be available after the 
  //server response, and after the page is rendered
  const pin = () => {
  const [result, setResults] = useState([]);
  var url = "http://warm-hamlet-63390.herokuapp.com/pin/list"

  useEffect(() => {
    //Attempt to retreive data
    try {
      const res = transformData();

      if (res) {
        // Add any data transformation
        setResults(transformData(res))
      }
      else {
        throw (error)
      }
    }
    catch (error) {
      //Handle error
    }
  }, [])

  // Handle data transformation
  const transformData = async () => {
    const res = await axios.get(url)

    const txt = JSON.stringify(res.data.data)
    const result = JSON.parse(txt)

    return result
  }

  if (!result) {
    // Return something until the data is loaded (usually a loader)
    return null
  }

  // Return whatever you would like to return after response succeeded
  return <></>;
}

This is all assuming that Pin is a component like you have shown in your code, alternatively, the call can be moved up to the parent component and you can add an inline check like below to render the pin and pass some data to it.

{result && <Pin property={someData} />}

Just a bit of background the useEffect hook has an empty dependency array shown at the end "[]" this means it will only run once, then once the data has updated the state this will cause a rerender and the change should be visible in your component

Vuk
  • 693
  • 4
  • 14
  • thanks for trying to help me. I get this error: ```Unexpected reserved word 'await'``` maybe because there is no "async" in the code you posted, but I don't know where to put it either – Enguias Jun 12 '21 at 14:51
  • Can't use `await` without declaring the containing function as `async` – drrkmcfrrk Jun 12 '21 at 15:01
  • Yes sorry, I forgot about that I have updated the code above and also moved the axios to the function as React doesn't like you using async in `useEffect` hook. – Vuk Jun 12 '21 at 15:02
  • Otherwise you could just add the async like this `useEffect(async () => {}` – Vuk Jun 12 '21 at 15:03
  • @VukVucic that actually doesn't work. But there's ways around it, see https://stackoverflow.com/a/56838577/9830755 or my answer. – drrkmcfrrk Jun 12 '21 at 15:04
  • Aysnc does not seem to work directly on the useEffect but you can put async on an anonymous function inside useEffect, as dirkenstocks has shown in their example. – Steve Tomlin Jun 13 '21 at 08:19
1

Rest assured, useEffect() will work. You need to use a condition to conditionally render the content when it comes back from the server.

In the example below if results has a length < 1 the message Loading ... will be rendered in the containing <div>, once you're results are received the state will be updated (triggering a re-render) and the condition in the template will be evaluated again. This time though results will have a length > 1 so results will be rendered instead of Loading ...

I’m operating under the assumption that you’re function pin() is returning the results array.

const app = (props) => {
    
  const [results, setResult] = useState([]);
    
  React.useEffect(() => {
    const getPin = async () => {
      if (!results) {
        const results = await pin();
        setResult([…results])
      }
    }
    getPin();
  },[results]);
    
  return (
    <div>
      {result.length ? result : 'Loading ... '}
    </div>
  )
}
drrkmcfrrk
  • 328
  • 1
  • 13