onSearch fetch coingecko api

586 Views Asked by At

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;
1

There are 1 best solutions below

6
On

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.