0

I am trying to build a React project where I have a component that fetches data from a remote server and plots it.

async function fetchRemoteData(name) {
  let dataItem = null;
  if (name) {
    await fetch("/endpoint/" + name)
      .then((res) => res.json())
      .then((payload) => {
        console.log("payload = " + JSON.stringify(payload));
        dataItem = payload;
      });
  } else {
    console.log("Error: No data for " + name);
  }
  return dataItem;
}

var RenderPlot = ({ match }) => {
    let remoteObject = fetchRemoteData(match.params.data);
    console.log("remoteObject = " + JSON.stringify(remoteObject));
    
    // .... rendering code
    // .... and other irrelevant stuffs to this question
}

If I run this code, on the console, I'm seeing the remoteObject is empty, but the payload inside the function fetchRemoteData() is not.

remoteObject = {}
payload = {"data":"some value"}

Now, let's say I have data stored in the browser (which is obviously I'm not supposed to do, but anyway) and if I fetch it using a generic function call, I'm not having problem.

function fetchLocalData(name) {
    // read the data from a local js object
    // and return it
}

var RenderPlot = ({ match }) => {
    let remoteObject = fetchRemoteData(match.params.data);
    console.log("remoteObject = " + JSON.stringify(remoteObject));

    let localObject = fetchLocalData(match.params.data);
    console.log("localObject = " + JSON.stringify(lovalObject.m))
    
    // .... rendering code
    // .... and other irrelevant stuffs to this question
}

and the output --

localObject = 4
remoteObject = {}
payload = {"data":"some value"}

So, what's happening is the code gets the localData from fetchLocalData(), then calls fetchRemoteData() but it doesn't wait if the data is received or not. And then, keeps doing whatever in the rest of the code.

What I want is, the code should wait until the remoteData is received and then proceed to the next steps.

How do I do that?

3
  • 1
    fetchRemoteData returns a promise of data, not data itself. It’s asynchronous and needs another “await” or “then” to get it. “the code should wait until the remoteData is received and then proceed” - you can’t, that’s fundamental fact about React lifecycle. Since the component is functional, use useEffect hook. Commented Oct 9, 2020 at 11:30
  • 2
    Don't use JSON.stringify and you'll see that remoteObject is not an empty object but a promise Commented Oct 9, 2020 at 11:36
  • @Bergi I see, it's a promise. Commented Oct 9, 2020 at 15:11

2 Answers 2

2

I can recommend you to store your received data in state of component like example below. If I understood you correctly -

const RenderPlot = ({ match }) => {
  const [data, setData] = useState()

  const fetchData = useCallback(async name => {
    try {
      const response = await fetch("/endpoint/" + name);   
      setData(response.data); // or whatever, depends on how you set your API up.
    } catch (e) {
      console.log(e)
    }
  }, [])

  // in this case it works as componentDidMount;
  useEffect(() => {
    ;(async function() {
      await fetchData(match.params.data);
    })()
  }, [match.params])

  return (<div>...your jsx plot implementation</div>)
}

According to this flow you can compose two fetching,

  const fetch1 = useCallback(async () => {your first call api}, [])
  const fetch2 = useCallback(async () => {your second call api}, [])

  useEffect(() => {
    ;(async function() {
      await fetch1...
      await fetch2
    })()
  }, [match.params])

Or it can be like this

  const fetchData = useCallback(name => {
    try {
      const response1 = await fetch("/endpoint1/" + name);
      const response2 = await fetch("/endpoint2/" + name);

      // this line would be read after both of your responses come up. Here you can make some operation with both data.

      setData(response.data); // or whatever, depends on how you set your API up.
    } catch (e) {
      console.log(e)
    }
  }, [])
Sign up to request clarification or add additional context in comments.

12 Comments

Why not put everything in the useEffect? Makes error handling simpler.
yep, you are right, it depends on how you process data
but be sure, you can call two fetching in separate useEffect - then they would be called concurrently and in different time. you can call both of them inside one async useEffect - and second fetching will be waiting for first resonse,
That unnecessary wrapping in another function just makes it more complicated.
look, you cannot just call side effects in your functional component because of it cannot be performed correctly. In previous version of react you could call api(side effect) in componentDidMount method. In FC the proper way is to call it inside useEffect hook
|
0

The reason is that ...return dataItem executes before that promise resolve the request. You need to use async await:

async function fetchRemoteData(name) {
   let dataItem = null;
   if (name) {
      const res = await fetch("/endpoint/" + name);
      dataItem = await res.json();
        
   } else {
        console.log("Error: No data for " + name);
   }
 return dataItem;
}

5 Comments

The returned dataItem is still a promise, I'm getting this: remoteObject = [object Promise]
Yes, that is because fetchRemoteData is an async function
You can get the value using then block or async await, fetchRemoteData(match.params.data).then(data => console.log("remoteObject = ", data))
well, but I'm not going to do console.log() only, I need the data to plot. when I do let remoteData = fetchRemoteData(match.params.data).then(data => {return data;}) the returned value is still [object Promise].
You need to use the await var RenderPlot = async ({ match }) => { let remoteObject = await fetchRemoteData(match.params.data);

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.