0

I'm creating just a simple currency converter (React + Typescript). Here is my component code:

const App = () => {

  const [countries, setCountries] = useState<Array<CountriesProps>>([])
  const [currencies, setCurrencies] = useState<Currencies>({})
  
  const filteredCountries = async () => {
      const { data } = await axios.get('https://restcountries.eu/rest/v2/all')
      const answer: Array<CountriesProps> = data
      const filtered = answer.filter(country => {
        for (let i in currencies) {
          if(i === country.currencies[0].code) {
            return country
          }
        }
      })
      setCountries(filtered)
  }
  
  useEffect(() => {
    axios
      .get('https://api.frankfurter.app/currencies')
      .then(res => {
        setCurrencies(res.data)
      })
      .catch(err => {
        console.log(err)
      }) 
  }, [])

  useEffect(() => {
    filteredCountries()
  }, [])

  return (
    ...
  )
}

export default App

I come across the problem, during launching the app. After getting currencies information from the server I need to fetch countries information. After getting countries I need to filter them and put them in my state (countries) and send it to another component and so on. But during launch of the app filter function doesn't work and I got no filtered countries and so I don't have any info in my state. I think that filter function needs to be an asynchronous, so we need to wait before setting our state through setCountries function. How to do it properly in my case or I did all the logic wrong?

4
  • 2
    The two useEffect functions do not run in order. Try using [currencies] as dependence array for the 2nd one, that way it runs after the currencies get set. Commented Dec 24, 2020 at 11:47
  • @thx, helped. But in network tab in developer tools I see that they go in order, why is so that? Commented Dec 24, 2020 at 11:56
  • 1
    useState is async in React, therefore when the second useEffect executes, currencies is not filled yet, therefore your filter doesn't work. If you really want to do it this way, you may want to try to store currencies in useRef, but I wouldn't recommend doing it so, as it still may not be filled at that point, you're better of going with @ChrisG solution. Commented Dec 24, 2020 at 11:58
  • I think they do run in order, but 2nd useEffect won't wait for 1st useEffect to complete before starting. So, by the time 2nd useEffect runs it is possible it does not have the data. Commented Oct 13, 2023 at 9:13

3 Answers 3

1

As long as requested countries rely on fetched currencies and you don't seem to be using one without the another, you may stack .get()-requests accordingly or use respective async...await alternative:

fetchData = async () => {
      const currenciesResponse = await axios.get(currenciesEndpoint),
        currenciesData = await currenciesResponse.data,
        countriesResponse = await axios.get(countriesEndpoint),
        countriesData = await countriesResponse.data,
        filteredCountriesData = countriesData.filter(_country => {
          const {
            currencies: [{ code }]
          } = _country;
          return currenciesData[code];
        });
      setCurrencies(currenciesData);
      setCountries(filteredCountriesData);
    }

  useEffect(() => {
    fetchData();
  }, [])

Following is a full-blown demo as a proof-of-a-concept

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

Comments

0

See if this helps.

const [countries, setCountries] = useState<Array<CountriesProps>>([])
  const [currencies, setCurrencies] = useState<Currencies>({})
  
  const filteredCountries = async () => {
      const { data } = await axios.get('https://restcountries.eu/rest/v2/all')
      const answer: Array<CountriesProps> = data
      const filtered = answer.filter(country => {
        return currencies[country.currencies[0].code]
      })
      setCountries(filtered)
  }
  
  useEffect(() => {
    axios
      .get('https://api.frankfurter.app/currencies')
      .then(res => {
        setCurrencies(res.data)
      })
      .catch(err => {
        console.log(err)
      }) 
  }, [])

  useEffect(() => {
    filteredCountries()
  }, [currencies])

Comments

0

try using this:


const App = () => {

  const [countries, setCountries] = useState<Array<CountriesProps>>([])
  const [currencies, setCurrencies] = useState<Currencies>({})
  
  const filteredCountries = async () => {
      const res = await axios.get('https://api.frankfurter.app/currencies')
      // you don't need a state for currencies but in case you find a use case for it,
      // you're just setting the currencies here for future use cases.
      setCurrencies(res.data);
      const { data } = await axios.get('https://restcountries.eu/rest/v2/all')
      const answer: Array<CountriesProps> = data
      const filtered = answer.filter(country => {
        for (let i in res.data) {
          if(i === country.currencies[0].code) {
            return country
          }
        }
      })
      setCountries(filtered)
  }

  useEffect(() => {
    filteredCountries()
  }, [])

  return (
    ...
  )
}

export default App

Comments

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.