1

I have a react app like so:

import React, { useState } from 'react';
import axios from 'axios';

const Body = () => {
    const defaultVersions = [
        {
            version: '2.96.0',
            status: null,
        }
    ];

    const [versions, setVersions] = useState(defaultVersions);

    const getData = version => {
        axios.get(`https://cdn10/${version}/en-US/test.js`)
            .then(response => {
                console.log(response.status);
                return response.status;
            })
            .catch(err => {
                console.log('in here');
            })
    };

    const onClick = () => {
        setVersions(versions.map(item => {
            const { version } = item;
            return { version, status: getData(version) };
        }));
    };

    return (
        <>
            <button onClick={onClick}>Check Versions</button>
            <ul>
                {versions.map(item => {
                    const { version, status } = item;
                    return <li>{`${version}: ${status}`}</li>
                })}
            </ul>
        </>
    );
}

export default Body;

And the console.log in getData works, but when I try to use getData to set the new value in my state on the button click, it's returning undefined. I've tried making getData async/await, but that didn't work. Any insight is appreciated!

3 Answers3

1

You can use async/await syntax and Promise.all() method.

For example:

const getData = async (version) => {
  try {
    const response = await axios.get(
      `https://cdn10/${version}/en-US/test.js`
    );
    return response.status;
  } catch (error) {
    console.log(error);
    return false;
  }
};

const onClick = async () => {
  const newVersions = await Promise.all(
    versions.map(async (item) => {
      const { version } = item;
      const status = await getData(version);
      return { version, status };
    })
  );
  setVersions(newVersions);
};

To see the whole example, please see codesandbox.io.

PS: I forked your codesandbox and fixed errors.

Pluto
  • 4,177
  • 1
  • 3
  • 25
  • Hm...it looks like with the above answer, it still comes back as `undefined`. And it looks like `version` also becomes `undefined` :( – user18971245 Jul 20 '23 at 23:27
  • Can you share your code in codesandbox? – Pluto Jul 20 '23 at 23:28
  • Sure, let me try.. – user18971245 Jul 20 '23 at 23:29
  • Here's a link to the sandbox (I had to change the api for privacy reasons, but I found a public GET api to use instead - it still results in the same `undefined` behavior as the above code): https://codesandbox.io/s/affectionate-turing-j9rydx?file=/src/components/Body.js – user18971245 Jul 20 '23 at 23:36
  • Umm, in your codesandbox, axios api throws an error, so I can't get the correct result. but I think this answer can help you. I updated my answer - @user18971245 – Pluto Jul 20 '23 at 23:42
  • @user18971245, did you test this code? – Pluto Jul 20 '23 at 23:58
  • Thank you! The sandbox works, I think something else is making my api error out, will have to investigate that further but thanks for helping me get the response! – user18971245 Jul 21 '23 at 00:18
  • You're welcome. I'm glad you found the reason. – Pluto Jul 21 '23 at 00:20
0

Problem is that your getData is not returning anything, yes you are calling your function but you aren't returning the promise returned by your axios call. That's the first problem. Second problem is that you are expecting multiple results when you click the button since you are doing a map of versions, in other words it's not just one promise, there could be many so you need to await all of them before setting the values

I think it's cleaner if we write it this way

const getData = async (version) => {
try{
  const response = await axios.get(`https://cdn10/${version}/en-US/test.js`)
  return response.status;
} catch(e){
  console.log("Something went wrong")
  throw new Error(e)
}
       
};

const onClick = async () => {
 const myData = await Promise.all(
  versions.map(async (item) => {
      const { version } = item;
      const status = await getData(version)
      return { version, status };
    })
 )
 setVersions(myData)
}

This function can be improved but for sakes of this example this will work

Manuel Duarte
  • 644
  • 4
  • 18
0

First of all, you missed return in getData. In JavaScript the result of function that has no return is undefined. So it always returns undefiend.

When we return axios.get the return type is Promise. So we should use async await or .then. You can update your code like this.

import React, { useState } from 'react';
import axios from 'axios';

const defaultVersions = [
    {
        version: '2.96.0',
        status: null,
    }
];

const Body = () => {
    const [versions, setVersions] = useState(defaultVersions);

    const getData = version => axios.get(`https://cdn10/${version}/en-US/test.js`)
        .then(response => response.status)
        .catch(err => {
            console.log('in here');
        });

    const onClick = async () => {
        setVersions(
            await Promise.all(
                versions.map(async ({ version, status }) => ({
                    version,
                    status: (await getData(version)) ?? status,
                }))
            )
        );
    };

    return (
        <>
            <button onClick={onClick}>Check Versions</button>
            <ul>
                {versions.map(item => {
                    const { version, status } = item;
                    return <li key={version}>{`${version}: ${status}`}</li>
                })}
            </ul>
        </>
    );
}

export default Body;

PS: When rendering list elements, we need to define a key prop.

Hotaka
  • 513
  • 1
  • 3
  • 7