Exercise - Refresh the Prices on Click

Thanks for getting back to me… GitHub link:
https://github.com/jonsax6/coin-exchange

Let me know what you see!

Hi @jonsax

I’ve found your issue.

There was a mistake in the tickerId passing in your handleRefresh call. Instead of calling https://api.coinpaprika.com/v1/tickers/btc-bitcoin, you were calling https://api.coinpaprika.com/v1/tickers/BTC. It is the incorrect API URL and since coinparika has a CORS policy on any of their server method calls, it was restricting you from accessing the non-public API URLs.

This issue was found at line number 14 of Coin.jsx component.

Previously it was this.props.handleRefresh(this.props.ticker)
I changed it to this.props.handleRefresh(this.props.tickerId); and it worked as expected.

Always analyse what URLs you are hitting through the network tab to properly debug your application.

Hope this helps.

1 Like

Thanks man!

By the way, I’m curious why I can’t increase my coin count to anything larger than 10? Do you know why this would cause a bug? If I wanted to have the count be 50 or 100, it throws an error. Even changing from 10 to 11 throws a 429 error. Is 10 the maximum number of requests for https://api.coinpaprika.com/v1/tickers/ ??

Debugger throws at: const promises = coinIds.map(id => axios.get(tickerUrl + id));

Same problem here, thanks for the catch!!

yeah, guessing that’s why he chose to limit the number of rows to 10. Maybe the API request throws a 429 after 10 times in a short period of time. But a market price feed page with only 10 rows isn’t very useful real-world actual. For the exercise it’s all good I guess. Anyway some confirmation from admin’s on this would be helpful in ruling out a bug vs API request limit… What say you admins!!?

Yes, they limit their API calls if you’re using their APi’s for free. However, if you buy their subscription they will allow a more elaborate throughput and can use that in the production environment.

Hope this helps.

const tickerUrl = 'https://api.coinpaprika.com/v1/tickers/'

refreshPrice = (targetTicker) => {
      //const coin = this.state.coinData.find( ({ticker}) => ticker === targetTicker )
      //console.log(coin)

      // Zsolt Nagy solution: it follows IMMUTABILITY 'cause it make a copy of the changed object (coinData)
      const newCoinData = this.state.coinData.map ( ( values ) => {
        // Shallow copy http://www.zsoltnagy.eu/cloning-objects-in-javascript/
        let newValues = { ...values }
        if ( targetTicker === values.ticker ) {
          //const tickerUrl = 'https://api.coinpaprika.com/v1/tickers/'          
          axios.get(tickerUrl + values.key)
          .then( response => {
            const coin = response.data
            console.log("App: tickerUrl", coin)
            newValues.price = parseFloat(Number(coin.quotes.USD.price).toFixed(5))
          })
          //const randomPercentage = 0.995 + Math.random() *0.01
          //newValues.price *= randomPercentage    
        }
        return newValues
      } )
      this.setState( {coinData: newCoinData} )
}
2 Likes

Like a couple others in here, I spent many hours trying to force ‘key’ as a prop into Coin and lift it up to the App.js refreshHandler to check against ‘values.key’, but that is not allowed!

After learning from you guys, I passed a new prop 'id={key} into my Coin component and lifted that to refreshHandler instead.

App.js
class App extends React.Component {
  state = {
    balance: 10000,
    showBalanceState: true,
    coinData: []
  }
  componentDidMount = async () => {
    const response = await axios.get(CoinsURL);
    const coinIds = response.data.slice(0,CoinCount).map(coin => coin.id);
    const promises = coinIds.map((id) => axios.get(CoinsURL + id));
    const coinData  = await Promise.all(promises);
    const coinDetails = coinData.map(function(response) {
      const coin = response.data;
      return{
        key: coin.id,
        name: coin.name,
        ticker: coin.symbol.toUpperCase(),
        balance: 0,
        price: parseFloat(Number(coin.market_data.current_price.usd).toFixed(4)),
      }
    });

      this.setState({coinData: coinDetails});
    };   
    handleShowBalance = () => {
      if(this.state.showBalanceState){this.setState({showBalanceState: false});}
      else if(!this.state.showBalanceState){this.setState({showBalanceState: true});}
    }

    handleRefresh = async (valueChangeId) => {
      const response = await axios.get(CoinsURL + valueChangeId);
      const newCoinData = this.state.coinData.map( function( values ) {
        let newValues = {...values};  // copies 'values' so we can change any as needed
        if (valueChangeId === values.key){
          newValues.price = parseFloat(Number(response.data.market_data.current_price.usd).toFixed(4))
        }
          return newValues;
       });
      this.setState({coinData: newCoinData});
    };
  
  render(){
    return (
      <Div className="App">
        <Header/>
        <AccountBalance amount={this.state.balance}
                        showBalance={this.state.showBalanceState} 
                        handleShowBalance={this.handleShowBalance}/>
        <CoinList coinData={this.state.coinData}
                  showBalance={this.state.showBalanceState}
                  handleRefresh={this.handleRefresh}
                  handleShowBalance={this.handleShowBalance}/>
      </Div>
    );
  }
}

export default App;


CoinList.js
export default class CoinList extends Component {
    render() {
      const balance = this.props.showBalance ?
        <th>Balance</th> : null;
        return (
        <CoinTable>
            <thead>
                <tr>
                  <th>Name</th>
                  <th>Ticker</th>
                  <th>Price</th>
                  <th>Action</th>
                  {balance}
                </tr>
            </thead>
            <tbody> 
              {
                this.props.coinData.map(({key,name,ticker,price,balance}) =>
                 <Coin  key={key}
                        id={key}
                        handleRefresh={this.props.handleRefresh} 
                        name={name} 
                        ticker={ticker} 
                        price={price}
                        balance={balance}
                        showBalance={this.props.showBalance} /> 
                  )
                }
            </tbody>
        </CoinTable>
        )
    }
}

Coin.js
export default class Coin extends Component {
    handleClick = (event) => {
        event.preventDefault();
        this.props.handleRefresh(this.props.id);
    }

    render(){
        const balance = this.props.showBalance ?
            <Td>{this.props.balance}</Td> : null;

        return(
            <tr>
                <Td>{this.props.name} </Td>
                <Td>{this.props.ticker} </Td>
                <Td>${this.props.price} </Td>
                <Td>
                    <form>
                        <button onClick={this.handleClick}>Refresh</button>
                    </form>
                </Td>
                {balance}
            </tr>
        );
    }
} 

Coin.propTypes = {
    name: PropTypes.string.isRequired,
    ticker: PropTypes.string.isRequired,
    price: PropTypes.number.isRequired,
    balance: PropTypes.number.isRequired
}


2 Likes

key is a special property that is mandatory for rendering lists (arrays) of components. When you use the name key, it won’t be available in the props. Also be careful about using id, because it is the HTML id attribute that has to be unique in a document.

2 Likes

const COIN_COUNT = 10;
const coinsUrl = ‘https://api.coinpaprika.com/v1/coins’;
const tickerUrl = ‘https://api.coinpaprika.com/v1/tickers/

class App extends React.Component {
state = {
balance: 10000,
showBalance: true,
coinData: []
};
componentDidMount = async () => {
const response = await axios.get(coinsUrl);
const coinIds = response.data.slice(0, COIN_COUNT).map(coin => coin.id);
const promises = coinIds.map(id => axios.get(tickerUrl + id));
const coinData = await Promise.all(promises);
const coinPriceData = coinData.map(function (response) {
const coin = response.data;
return {
key: coin.id,
name: coin.name,
ticker: coin.symbol,
balance: 0,
price: parseFloat(Number(coin.quotes[‘USD’].price).toFixed(4)),
};
})
//Retrieve the prices
this.setState({ coinData: coinPriceData });
};

handleRefresh = async (valueChangeKey) => {
const coinValue = valueChangeKey
console.log(coinValue);
const keyData = await axios.get(tickerUrl + valueChangeKey);
console.log(keyData);
const newCoinData = this.state.coinData.map(function (values) {
let newValues = { …values };
if (valueChangeKey === values.key) {

    newValues.price = parseFloat(Number(keyData.data.quotes['USD'].price).toFixed(2))
  };
  return newValues;

});

this.setState({ coinData: newCoinData });

}
handleToggleShowBalance = () => {
this.setState(function (oldState) {
return {
…oldState,
showBalance: !oldState.showBalance
};
});
}

render() {

return (
  <Div>
    <Header />
    <AccountBalance
      amount={this.state.balance}
      showBalance={this.state.showBalance}
      handleToggleShowBalance={this.handleToggleShowBalance} />
    <CoinList
      coinData={this.state.coinData}
      handleRefresh={this.handleRefresh}
      showBalance={this.state.showBalance} />
  </Div>
);

};
}

export default App

2 Likes

I’m using this course to help me build my Dex project from the Smart Contract 202 course, and I’ve used the coingecko api instead…so here is my code :slight_smile:

  getGecko = async () => {
    let url = "https://api.coingecko.com/api/v3/simple/price?ids=ethereum%2Cchainlink&vs_currencies=gbp,eth&include_market_cap=true&include_24hr_change=true"
    let response = await axios.get(url);
      let jsonData = JSON.stringify(response);
      this.setGecko(jsonData);
    };
  
  setGecko = (data) => {
    let info = JSON.parse(data);
    let coinData = [
            {
              name: 'Ethereum',
              ticker: 'ETH',
              priceETH: 1,
              priceGBP: info.data.ethereum.gbp              
            },
            {
              name: 'Chainlink',
              ticker: 'LINK',
              priceETH: info.data.chainlink.eth,
              priceGBP: info.data.chainlink.gbp              
            }
          ];
    this.setState({ coinData });
  };

and then in the componentDidMount function…

this.getGecko();
      const callback = () => {
          this.getGecko();
        };
      setInterval(callback,10000);

So the prices refresh every 10 seconds.

1 Like

Hi, @n1g3.

Splendid!

Keep up the great work! :muscle:

Happy Learning!

This was pretty confusing for me but I’m finally starting to understand.
in CoinList.jsx we add a new property id = {key}.

In Coin.jsx we pass this.props.handleRefresh(this.props.id); in the handleClick function. So the coins key is passed to app.js since id = key.

in app.js the coins id prop is passed to (ValueChangeId). this way we can just add it to the end of our api call to get the right coin info, ex : axios.get(tickerUrl + valueChangeId);

also we use ValueChangeid (which is a certain coins key) with values.key and whichever one is matching is how the code knows to update price with our if statement.

app.js
import React from 'react';

import CoinList from './components/CoinList/CoinList';

import AccountBalance from './components/AccountBalance/AccountBalance';

import Header from './components/Header/Header';

import styled from 'styled-components';

import axios from 'axios';

const Content = styled.div`

text-align: center;

  background-color: rgb(82, 79, 79);

  color: rgb(180, 178, 19);

`;

const COIN_COUNT = 10;

const tickerUrl = 'https://api.coinpaprika.com/v1/tickers/';

class App extends React.Component {

  state = {

    balance: 10000,

    showBalance: true,

    coinData: [

/*

      {

        name: 'Bitcoin',

        ticker: 'Btc',

        balance: 0.5,

        price: 9999.99

      },

      {

        name: 'Ethereum',

        ticker: 'Eth',

        balance: 35,

        price: 2999.99

      },

      {

        name: 'Tether',

        ticker: 'USDT',

        balance: 0.5,

        price: 1.0

      },

      {

        name: 'Ripple',

        ticker: 'XRP',

        balance: 1000,

        price: 1.3

      },

      {

        name: 'Bitcoin Cash',

        ticker: 'BCH',

        balance: 0,

        price: 298.99

      }, */

    ]

  }

  componentDidMount = async () => {

  const response = await axios.get('https://api.coinpaprika.com/v1/coins');

  const coinIds = response.data.slice(0, COIN_COUNT).map(coin => coin.id);

  

  const promises = coinIds.map(id => axios.get(tickerUrl + id));

  const coinData = await Promise.all(promises);   

  const coinPriceData = coinData.map(function(response) {

    const coin = response.data;

    return {

      key: coin.id,

      name: coin.name,

      ticker: coin.symbol,

      balance: 0,

      price: parseFloat(Number(coin.quotes.USD.price).toFixed(4)),

    };

  })

  // Retrieve the prices

  this.setState({ coinData: coinPriceData });     

  }

  handleRefresh = async (valueChangeId) => {

    let response = await axios.get(tickerUrl + valueChangeId);

    const refreshedData = response.data;

    const newCoinData = this.state.coinData.map( function( values ) {

      let newValues = {...values};

      if (valueChangeId === values.key) {

        newValues.price = parseFloat(Number(refreshedData.quotes['USD'].price).toFixed(2));

      }

      return newValues;

    });

    this.setState({ coinData: newCoinData });

  }

     handleBalanceToggle = () => {

       this.setState( function(oldState) {

         return {

           ...oldState,

           showBalance: !oldState.showBalance

         }

       });

     }

  render() {

    return (

      <Content>

        <Header />

        <AccountBalance amount={this.state.balance}

          showBalance={this.state.showBalance}

          handleBalanceToggle={this.handleBalanceToggle} />

       <CoinList coinData={this.state.coinData}

        handleRefresh={this.handleRefresh}

        showBalance={this.state.showBalance} />

      </Content>

    );

  }

  

}

export default App;
Coin.jsx
import React, { Component } from 'react';

import PropTypes from 'prop-types';

import styled from 'styled-components';

const CoinRow = styled.td`

border: 1px solid;

    width: 25vh;

`;

export default class Coin extends Component {

    

    handleClick = (event) => {

        // Prevent the default action of submitting the form

        event.preventDefault();

        this.props.handleRefresh(this.props.id);

    }  

    render() {

        return (

            <tr>

              <CoinRow>{this.props.name}</CoinRow>

              <CoinRow>{this.props.ticker}</CoinRow>

              <CoinRow>${this.props.price}</CoinRow>

              {this.props.showBalance ? <CoinRow>{this.props.balance}</CoinRow> : null}

              <CoinRow>

                  <form action="#" method="POST">

                  <button onClick={this.handleClick}>Refresh</button>

                  </form>            

              </CoinRow>

            </tr>

           );

    }

}

Coin.propTypes = {

    name: PropTypes.string.isRequired,

    ticker: PropTypes.string.isRequired,

    price: PropTypes.number.isRequired,

    balance: PropTypes.number.isRequired

}
CoinList.jsx
import React, { Component } from 'react';

import Coin from "../Coin/Coin";

import styled from 'styled-components';

const Table = styled.table`

margin: 50px auto 50px auto;

  display: inline-block;

  font-size: 1.4rem;

`;

export default class CoinList extends Component {

    render() {

        return (

            <Table>

        <thead>

          <tr>

            <th>Name</th>

            <th>Ticker</th>

            <th>Price</th>

            {this.props.showBalance ? <th>Balance</th> : null}

            <th>Actions</th>

          </tr>

        </thead>

        <tbody>

          {

            this.props.coinData.map( ({key, name, ticker, balance, price}) => 

            <Coin key={key} 

            id={key}

            handleRefresh={this.props.handleRefresh} 

            name={name} 

            ticker={ticker} 

            showBalance={this.props.showBalance}

            balance={balance}

            price={price}  />,

            )

          }

        </tbody>

      </Table>

        )

    }

}

also i got some help from the toshi times here

2 Likes

Here’s my answer.
I don’t know if I did it correctly because CoinPaprika API may not be updating every second.
Also, I added coinID props to the Coin component.

App.js
import React from 'react';
import ExchangeHeader from './components/ExchangeHeader';
import CoinList from './components/CoinList';
import AccountBalance from './components/AccountBalance';
import styled from 'styled-components';
import axios from 'axios';

const Div = styled.div`
  text-align: center;
  background-color: rgb(20,56,97);
  color: #cccccc;
`;

const COIN_COUNT = 10;

class App extends React.Component {
  state = {
    balance: 10000,
    balanceVisibility: true,
    coinData: [

    ]
  }

  componentDidMount = async () => {
    const rawData = await axios.get('https://api.coinpaprika.com/v1/coins');
    const coinIds = rawData.data.slice(0, COIN_COUNT).map( coin => coin.id );
    const tickerUrl = 'https://api.coinpaprika.com/v1/tickers/';
    const promises = coinIds.map(id => axios.get(tickerUrl + id));
    const coinData = await Promise.all(promises);
    const coinPriceData = coinData.map((response) => {
      const coin = response.data;
      return {
        key: coin.id,
        name: coin.name,
        ticker: coin.symbol,
        balance: 0,
        price: parseFloat(coin.quotes['USD'].price.toFixed(4))
      }
    });
    this.setState({coinData: coinPriceData});
  }

  handleRefresh = (valueChangeCoin) => {
    const newCoinData = this.state.coinData.map((values) => {
      let newValues = {...values};
      if(valueChangeCoin.ticker === newValues.ticker){
        axios.get(`https://api.coinpaprika.com/v1/tickers/${valueChangeCoin.coinId}`)
        .then(res => {
          const newPrice = res.data.quotes['USD'].price;
          console.log(newPrice);
          newValues.price = parseFloat(newPrice.toFixed(4));
        });
      }
      return newValues;
    });
    
    this.setState({ coinData: newCoinData });
  }

  handleBalanceVisibility = () =>{
    this.setState(prevState => ({balanceVisibility: !prevState.balanceVisibility}));
  }

  render(){
    return (
      <Div className="App">
        <ExchangeHeader/>
        <AccountBalance 
          amount={this.state.balance} 
          showBalance={this.state.balanceVisibility} 
          handleVisibility={this.handleBalanceVisibility}
        />
        <CoinList coinData={this.state.coinData} showBalance={this.state.balanceVisibility} handleRefresh={this.handleRefresh}/>
      </Div>
    );
  }
}

export default App;

CoinList.jsx
import React, { Component } from "react";
import Coin from './Coin';
import styled from 'styled-components';

const Table = styled.table` 
  margin: 50px auto 50px auto;
  display: inline-block;
  font-size: 1.4rem;
`;


export default class CoinList extends Component {
  render() {
    return (
      <Table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Ticker</th>
            <th>Price</th>
            <th>Balance</th>
            <th>Actions</th>
          </tr>
        </thead>
        <tbody>
          {this.props.coinData.map(({ key, name, ticker, price, balance }) => (
            <Coin key={key} handleRefresh={this.props.handleRefresh} name={name} balance={this.props.showBalance ? balance : "*****"} ticker={ticker} price={price} coinId={key}/>
          ))}
        </tbody>
      </Table>
    );
  }
}

Coin.jsx
import React, { Component } from "react";
import PropTypes from "prop-types";
// import "./Coin.css";
import styled from 'styled-components';

const Td = styled.td`
  border: 1px solid #cccccc;
  width: 25vh;
`;

export default class Coin extends Component {
  handleClick = (event) => {
    event.preventDefault();
    this.props.handleRefresh(this.props);
  }

  render() {
    return (
      <tr className="coin-row">
        <Td>{this.props.name}</Td>
        <Td>{this.props.ticker}</Td>
        <Td>${this.props.price}</Td>
        <Td>{this.props.balance}</Td>
        <Td>
          <button onClick={this.handleClick}>Refresh</button>
        </Td>
      </tr>
    );
  }
}

Coin.propTypes = {
  name: PropTypes.string.isRequired,
  ticker: PropTypes.string.isRequired,
  price: PropTypes.number.isRequired,
};

I had to use .then method here because map doesn’t allow you to use await

2 Likes

Here is the main app component snippet.
Also, I’m passing key as id prop to coin component from CoinList

handleRefresh = async(clickedId) => {
     const response = await axios.get(`https://api.coinpaprika.com/v1/tickers/${clickedId}`);
     const newCoinData = this.state.coinData.map(function(value){
     var newValue = {...value};
       if(newValue.key === clickedId){
          var coin = response.data;
          newValue.price = parseFloat(Number(coin.quotes.USD.price).toFixed(9));
       }
       return newValue;
     });
     this.setState({coinData : newCoinData});
  }
2 Likes
handleRefresh = async (valueChangeKey) => {
    const coin = await axios.get('https://api.coinpaprika.com/v1/tickers/' + valueChangeKey);
    const newCoinData= this.state.coinData.map(function(values){
      let newValues = {...values};
      if ( valueChangeKey === values.key) {
        newValues.price = parseFloat(coin.data.quotes.USD.price.toFixed(4));
        console.log(newValues.price)
      }
      return newValues;
    

    });
    this.setState({coinData: newCoinData});

  }
2 Likes

My solution to refresh prices via refresh button, complete app.js.
Changed: some extra const’s in top and the doCoinRefresh function

I discovered situation that the asynchronous call to axios lets met get the new price in the old code situation, where i replaced the inner code of the if with the call to axios with the ticker url, but at the end (outside the if with the axios call) the this.setState() fills with empty vars! Logical because that line is processed before axios returned the new values(!). So i changed the code as here shown, with this.setState directly after the axios awaited call. So I copied first complete coinData array block, loop until selected row is found , request and update the price and replace coinData with copied, changed array and break the for-loop, since only 1 row needs to be refreshed, further rows skipping.

import React from 'react';
import './App.css';
import AppHeader from './components/AppHeader/AppHeader';
import CoinList from './components/CoinList/CoinList';
import AccountBalance from './components/AccountBalance/AccountBalance';
import axios from 'axios';

const COIN_COUNT = 5;
const COINS_URL = 'https://api.coinpaprika.com/v1/coins';
const TICKER_URL = 'https://api.coinpaprika.com/v1/tickers/';
const DECIMALS = 6;

class App extends React.Component {
  state = {
    balance: 1000,
    showBalance: false,
    coinData: []
  }

  componentDidMount = async () => {
    const response = await axios.get(COINS_URL);
    const coinIds = response.data.slice(0, COIN_COUNT).map( coin => coin.id );
    const promises = coinIds.map( id => axios.get(TICKER_URL + id));
    const coinData = await Promise.all(promises);
    const coinPriceData = coinData.map( response => {
      const coin = response.data;
      return {
        key: coin.id,
        name: coin.name,
        ticker: coin.symbol,
        price: parseFloat(Number(coin.quotes['USD'].price).toFixed(DECIMALS)),
        balance: 0,        
      };
    });
    this.setState( {coinData: coinPriceData} );
  }
  //componentDidUpdate = () => {
  //  console.log('componentDidUpdate');
  //}

  doCoinRefresh = async (selectedTicker) => {
    let newCoinData = this.state.coinData;
    for (var key in newCoinData) {
      if (newCoinData[key].ticker === selectedTicker) {
        const ticker = await axios.get(TICKER_URL + newCoinData[key].key);
        newCoinData[key].price = parseFloat(Number(ticker.data.quotes['USD'].price).toFixed(DECIMALS));
        this.setState({coinData: newCoinData}); // replaces only mentioned part
        break;
      }
    };
  }

  doBalanceDisplay = (setBalanceDisplay) => {
    this.setState({showBalance: setBalanceDisplay});
  }

  render() {
    return (
      <div className="App">
        <AppHeader />
        <AccountBalance amount={this.state.balance} showBalance={this.state.showBalance} doBalanceDisplay={this.doBalanceDisplay} />
        <CoinList coinData={this.state.coinData} showBalance={this.state.showBalance} doCoinRefresh={this.doCoinRefresh} />
      </div>
    );
  }
}

export default App;
2 Likes

Refresh Price on Click proposed solution.

App.jsx

import AccountBalance from './components/AccountBalance/AccountBalance';
import React from 'react'; 
import CoinList from './components/CoinList/CoinList.jsx';
import Header from './components/Header/Header.jsx';
import axios from 'axios';

const COIN_COUNT = 10;
const formatPrice = price => parseFloat(Number(price).toFixed(4));

class App extends React.Component{
  //state is a classProperty it = value
  state = {
      balance: 10000,
      showBalance: false,
      coinData: []
  };

  componentDidMount = async () =>{
    const response = await axios.get('https://api.coinpaprika.com/v1/coins')
    const coinIds = response.data.slice(0, COIN_COUNT).map( coin =>  coin.id)
      //retrieve the coin prices
    const tickers = 'https://api.coinpaprika.com/v1/tickers/';
    const promises = coinIds.map(id => axios.get(tickers + id));
    const coinData = await Promise.all(promises);
    const coinPriceData = coinData.map( response => {
     const coin = response.data;
      return {
        key: coin.id,
        name: coin.name,
        ticker: coin.symbol,
        balance: 0,
        price: formatPrice(coin.quotes.USD.price),
      };
    });
    
      this.setState({coinData: coinPriceData});
  };


  handleBalanceVisability = (Event) => {
    Event.preventDefault();
    
    this.setState( oldState => {
    return { 
            ...oldState,
             showBalance: !oldState.showBalance
           }
    });
  }

  handleRefresh = async (valueChangedId) => {
    const tickers = `https://api.coinpaprika.com/v1/tickers/${valueChangedId}`;
    const response = await axios.get(tickers);
    const newPriceAmount = formatPrice(response.data.quotes.USD.price);

    const newCoinData = this.state.coinData.map(({key, ticker, name, price, balance}) => {
      let newPrice = price;
      if(valueChangedId === key){
        
        newPrice = newPriceAmount;
      }
      return {
        key,
        ticker,
        name,
        price: newPrice,
        balance
      }
      });
      //console.log(newCoinData);
      this.setState({coinData: newCoinData})
  }
  render()
    {
        return (
          <>
            <Header />
            <AccountBalance 
                amount={this.state.balance} 
                showBalance={this.state.showBalance} 
                handleBalanceVisability={this.handleBalanceVisability}
            />
            <CoinList 
                coinData={this.state.coinData} 
                showBalance={this.state.showBalance}
                handleRefresh={this.handleRefresh} 
            />
          </>
      
      );
  }
}

export default App;

CoinList.jsx

import React, { Component } from 'react';
import styled from 'styled-components';
import Coin from '../Coin/Coin.jsx';
import { v4 as uuidv4 } from 'uuid';


const Table = styled.table`

    margin: 50px auto 50px 250px;
    display: inline-block;
    font-size: 1.2rem;

    `;

export default class CoinList extends Component{

    render(){
        return(
            <Table>
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Ticker</th>
                        <th>Price</th>
                       {this.props.showBalance ? <th>Balance</th> : null }
                    </tr>
                </thead>
                <tbody>
                    {
                        this.props.coinData.map( ({ key, name, ticker, price, balance}) => 
                        <Coin 
                            key={uuidv4()}
                            handleRefresh={this.props.handleRefresh}
                            name={name} 
                            ticker={ticker}
                            showBalance={this.props.showBalance}
                            balance={balance} 
                            price={price}
                            tickerId={key}
                          />)
                    }

                </tbody>
            </Table>
        );
    }
} 

Coin.jsx

import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled from 'styled-components';



const Button = styled.button`
   background: black;
   border-radius: 3px;
   border: 2px solid darkgrey;
   color: white;
   margin: 0 1em;
   padding: 0.25em 1em;
  
    `;


    const Td = styled.td`
       border: 1px solid black;
       background-color: lightgrey;
       width: 25vh; 
    `;


export default class Coin extends Component {


   handleClick = (Event) => {
       Event.preventDefault();

       this.props.handleRefresh(this.props.tickerId);
   }

   
    render(){
        return (
            <tr>
                <Td>{this.props.name}</Td>
                <Td>{this.props.ticker}</Td>
                <Td>${this.props.price}</Td>
                {this.props.showBalance ? <Td>{this.props.balance}</Td> : null}
                <Td>
                    <form action="#" method="POST">
                        <Button onClick={this.handleClick}>Refresh</Button>
                    </form>
                </Td>
            </tr>
        );
    }
    
}
//type checking.
Coin.propTypes = {
    name: PropTypes.string.isRequired,
    ticker: PropTypes.string.isRequired,
    price: PropTypes.number.isRequired,
    balance: PropTypes.number.isRequired
}

2 Likes

When working on this one I had to watch the solution video more than once and pause in the middle because of scheduling. I learned an important lesson about structuring and mistakes once the code gets long. It can be easy to lose track of what means what especially if you delete the wrong lines of code and replace them and realize after the fact. This one was exciting, now I have a real working price website of my own!

2 Likes
const tickerURL = 'https://api.coinpaprika.com/v1/tickers/';

handleRefresh = (valueChangeTicker) => {
     const newCoinData = this.state.coinData.map(function (values) {
      let newValues = {...values};
      if (valueChangeTicker === values.ticker) {
        axios.get(tickerURL+values.key)
        .then(response => {
          const coin = response.data;
          newValues.price = parseFloat(Number(coin.quotes.USD.price).toFixed(2));
        })
         
      }
      return newValues;

     });
     this.setState({coinData: newCoinData});
  }

  toggleBalance = () => {
    this.setState({showBalance: !this.state.showBalance});
  }
2 Likes