0

I'm trying to implement onSearch fetch functionality on coingecko api.So apparently i'm doing everything right.

onLoadMore button is working fine, everything is ok until i enter something in searchbar, then app crashes with error'coins are not iterable'.

I verified the request(https://api.coingecko.com/api/v3/search?query=bitcoin) with postman and it seems to give me back the right response.

Thank you.

import React, { useState, useEffect, useRef } from 'react';
import { Link } from 'react-router-dom';
import axios from 'axios';

import CoinItem from '../CoinItem/CoinItem';
import Coin from '../routes/Coin';
import Spinner from '../Spinner/Spinner'
import searchIcon from './search-3-32.ico'

import './Coins.css';


const Coins = () => {

    const [isloading, setIsLoading] = useState(true)
    const [coins, setCoins] = useState([]);
    const [page, setPage] = useState(1);
    const [searchText, setSearchText]= useState()


    // const url = 'https://api.coingecko.com/api/v3/coins/markets?vs_currency=eur&order=market_cap_desc&per_page=100&page=1&sparkline=false



    const loadMore = () => {
        setPage(page => page + 1)
    }

    const fetchAll = async () => {
       if(searchText !== '') {
           await axios.get(`https://api.coingecko.com/api/v3/search?query=${searchText}`).then((response) => {
               setCoins(response.data)
               console.log(response)
           }).catch(error => {
               console.log(error)
           })
       }
       await axios.get(`https://api.coingecko.com/api/v3/coins/markets?vs_currency=eur&order=market_cap_desc&page=${page}&per_page=10`).then((response) => {
            setCoins(coins => [...coins, ...response.data])
            // console.log(response)
        }).catch(error => {
            console.log(error)
        })
        setIsLoading(false)
    }

    useEffect(() => {
        fetchAll()
    }, [page, searchText])




    if (isloading) {
        return <Spinner />
    }

    return (
        <div className='container'>
            <div>

                <div className='heading'>
                    <img src={searchIcon} alt="icon" />
                    <input
                        // ref={initial}
                        className='search'
                        type="text"
                        value={searchText}
                        placeholder='Search coin'
                        onChange={(e) => setSearchText(escape.target.value)}
                    />
                </div>

                <div className='heading'>
                    <p>#</p>
                    <p className='coin-name'>Coin</p>
                    <p>Price</p>
                    <p>24h</p>
                    <p className='hide-mobile'>Volume</p>
                    <p className='hide-mobile'>Mkt Place</p>
                </div>

                    {coins.map(coins => {
                        return (
                            <Link to={`coin/${coins.id}`} element={<Coin />} key={coins.id}>
                                <CoinItem coins={coins} />
                            </Link>
                        )
                    })}
                <button className='load-more' onClick={loadMore}>Load More</button>
            </div>
        </div>
    );
};

export default Coins;
VLAZ
  • 26,331
  • 9
  • 49
  • 67
  • you're not returning after the search, so it's also calling the next bit of code. And the value 'coins' from setCoins() will not be set immediately. It takes it's value once you return control to the render loop. – Dave Mar 28 '22 at 14:43

1 Answers1

0

You're not returning after the search, so it's also calling the next bit of code. And the value 'coins' from setCoins() will not be set immediately. It takes it's value once you return control to the render loop.

But I think the main reason for your error is that /coins/market returns an array, while /search is returning an object with a key 'coins' that holds the array. So, for the search api, you want response.data.coins.

UPDATE

I pulled your GitHub repo into this code sandbox: https://codesandbox.io/s/magical-napier-f0jbx4

You're very far along and the UI looks great. I left some comments at the top of the coins file. As I said earlier, do not mix & match async/await and .then() .catch(). The .then() will resolve the promise, so you do not need to await it.

You were loading all coins every time the search text changed, in addition to searching for coins. That resulted in your array of coins growing and growing - which in turn is why you were getting warnings about duplicate keys.

The project is working now... sort-of. Your actual issue is that the data you get back from "search" is very different from the data you get back from getting coins 1 page at a time. There were other issues, but that's the #1 problem. On the Crypto API documentation page, just test those 2 APIs and you see the response you get is very different, not just in structure - but the actual data returned.

I tweaked the "coin" component so it will not blow up when it gets "bad" data - so now you can see everything working, experiment, etc. To fix this, you will want to have "coins" for the list of all coins and "searchResultsCoins" for the search results. And when you have searchResults, show your search results UI instead of the all-coins UI.

Dave
  • 7,552
  • 4
  • 22
  • 26
  • if i return response.data.coins i receive 'coins is not iterable' error, with response.data i receive memory leak error – munteanu dan Mar 28 '22 at 15:11
  • If you're going to use .then() .catch() then don't use async/await. Missing them could be contributing to your issues. You are searching for `escape.target.value` and I think you intended `e.target.value`. The 'not iterable' message is telling you coins is not an array. It's probably undefined. Log it out. And log the results of those API calls... Return after the if (search) so you don't search, and then overwrite the results. – Dave Mar 28 '22 at 15:30
  • (escape.target.value) yes i changed it after the post, i also tried to use another useffect, and useRef to avoid memory leak but the res are same.. – munteanu dan Mar 28 '22 at 15:40
  • If you see a warning about a memory leak, it's probably because you destroy a component (leave the page) during a data fetch. To remedy that, you add an `let isCancelled = false` at the top of your useEffect. Then set it in the useEffects return function and check the isCancelled value before you call setCoins. And move the fetch function inside the useEffect so it's in the same scope as isCancelled. If you move your code to a code sandbox, we can fix it up quickly. Seems like you're close. Just debug log out the values so you see the 'actual' and not just what you think they should be. – Dave Mar 28 '22 at 16:01
  • Actually i did a console.log on the res.data.coins and i get the right answ(of 20 obj starting with bit). Maybe i need to put another condition then i map the coin – munteanu dan Mar 28 '22 at 16:03
  • With the sandbox it's a bit complicated, I've never posted before but here is the repo https://github.com/dandevmd/cryptoKyrc. I really appreciate your help. I spent my entire day trying to do this searchBar so you saved my laptop from being thrown out the window. Thank you very much – munteanu dan Mar 28 '22 at 16:48