React - FlatCoin
Published: 2021-06-25 23:58:35

Welcome to React - FlatCoin! This App was develop as a Flatiron School React project. I developed this app to help users practice trading cryptocurrencies using virtual (fake) money.
> First thing to mention, this app is an incomplete one. My original plan was to come up with a fully functional trading application but I didn't anticipate multiple tricky variables like calculating true and updated return on total investment and data collected to display charts. But I made sure to meet project requirements, this app is still a work in progress.

# Back End Setup:
Rails make it very easy to set up a backend API for a React-Project.
```ruby
rails new react-flatcoin-rails-backend --api
```
I generated my models via `scaffold`:
```ruby
rails generate scaffold user first_name last_name email password:digest
rails generate scaffold portfolio name description initial_balance:decimal current_balance:decimal user_id:integer
rails generate scaffold trade coin_name coin_id price:decimal quantity:integer user_id:integer portfolio_id:integer
```
I used serializers to easily control the way I'm displaying data as a JSON.
```ruby
gem 'active_model_serializers'
```
And finally I created a session controller to deal with User Authentication.  
```ruby
rails generate controller Session new --no-helper --no-assets --no-test-framework --skip-routes --skip
``` 
Check out my back end repository here on [GitHub](https://github.com/drivera53/react-flatcoin-rails-backend)!  
> **Note:** You may refer to my previous Rails project for more detailed information on Rails API [FlatCoin on Rails](https://dev.to/drivera53/flatcoin-on-rails-2d18).

# Front End:
First thing first, it's really easy to create the basic structure of a react App:
```ruby
npx create-react-app react-flatcoin
```
Then make sure to add Redux:
```ruby
npm install react-redux --save
```
When starting an App from scratch it makes sense to start off by visualizing your final product, it doesn't have to be exactly like your final product but at least have a good idea of the different components of your app. My app required a user logging in and playing around with its portfolio of cryptocurrencies, so for me it make sense to start off by setting up User Authorization. 
This part gave me such a hard time, but after 3 days of struggle I found this useful article about its setup [Using JWT in Your React+Redux App for Authorization](https://levelup.gitconnected.com/using-jwt-in-your-react-redux-app-for-authorization-d31be51a50d2) check it out, will save you hours.
 
My next challenge was to fully understand Middleware and asynchronous actions within React/Redux in order to start creating the functionality of my App, since my app required to fetch data from both my Rails API back end and live cryptocurrency data from [CoinGecko](https://www.coingecko.com/en/api). After some heavy research I found this [YouTube](https://www.youtube.com/watch?v=qA6oyQQTJ3I) video that helped me fully understand the whole process.         
In `index.js` I'm using `Provider` to create a global store to keep things simple, I'm also using `combineReducers` to combine `cryptosReducer` and `usersReducer` since they are being fetch from 2 different back ends.   
```js
// React
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';

// Imporing CSS
import './index.css';

// Redux - Thunk
import { Provider } from 'react-redux'
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import logger from 'redux-logger'

// Imporing Reducers
import { combineReducers } from 'redux'
import cryptosReducer from './reducers/cryptosReducer'
import usersReducer from './reducers/usersReducer'

import reportWebVitals from './reportWebVitals';

const rootReducer = combineReducers({
  user: usersReducer,
  crypto: cryptosReducer
})

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;

// Creating Store
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk, logger)))

ReactDOM.render(
  <React.StrictMode>  
    <Provider store={store}>
      <App />
    </Provider>
  </React.StrictMode>,
  document.getElementById('root')
);

reportWebVitals();
```
`logger` turned out to be an excellent tool to visualize the previous state and the next step based on the `action` that's been passed.
```ruby
npm install react-logger --save
```
Just add it as a second middleware when creating store:
```js
const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk, logger)))
```
On your console:
![Logger](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vd4igzfb5fltids5s253.png)
My `App.js` file is easy to follow, I created a `fetchEverything` function to deal with `fetchLoggedInUser` and `fetchCryptos` via `componentDidMount`.

I handle loading via `handleCryptoListLoading`'s logic, then render returns all its content.

`mapStateToProps` function maps proper state values and `mapDispatchToProps` dispatches its values, then I use `connect` to connect them both:
```js
export default connect(mapStateToProps, mapDispatchToProps)(App)
```
Full code:    
```js
import React, { Component } from 'react'
import { connect } from 'react-redux'
import { fetchCryptos } from './actions/cryptoActions'
import { fetchLoggedInUser } from './actions/userActions'
import { logOutUser } from './actions/userActions'
import CryptoList from './components/CryptoList'
import TradeCryptoList from './components/TradeCryptoList'
import NavBar from './components/NavBar'
import NotLoggedInNavBar from './components/NotLoggedInNavBar'
import Dashboard from './components/Dashboard'
import LoginForm from './containers/loginForm'
import SignUpForm from './containers/signUpForm'
import TradeCryptoHoldingDescription from './components/TradeCryptoDescription'
import TradeCryptoHistoryList from './components/TradeCryptoHistoryList'

import './App.css';

import Loading from './svg/Loading'

import {
  BrowserRouter as Router,
  Switch,
  Route
} from 'react-router-dom'

class App extends Component {

  fetchEverything = () => {
    this.props.fetchLoggedInUser()
    this.props.fetchCryptos()
  }

  componentDidMount() {
    this.fetchEverything()
  }

  handleCryptoListLoading = () => { console.log true or false
    if(this.props.loading) {
      return <Loading />
    } else {
console.log(this.props.current_user.first_name)
      return <CryptoList cryptoData={this.props.cryptoData} />
    }
  }

  handleTradeCryptoListLoading = () => { console.log true or false
    if(this.props.user_loading) {
      return <Loading />
    } else {
      return <TradeCryptoList cryptoData={this.props.cryptoData} current_user={this.props.current_user} />
    }
  }

  handleTradeCryptoHistoryListLoading = () => { console.log true or false
    if(this.props.user_loading) {
      return <Loading />
    } else {
      return <TradeCryptoHistoryList cryptoData={this.props.cryptoData} current_user={this.props.current_user} />
    }
  }

  logOut = () => {
    localStorage.removeItem("token")
    this.props.logOutUser()
    alert("Succesfully log out!")
  }

  render() {
    return (
      <>
        <div className="App">
          <Router>
            <div className="app__navBar">
              {this.props.login? <NavBar logOut = {this.logOut}/> : <NotLoggedInNavBar/> }
            </div>
            <Switch>

              <Route exact path="/">   
                <div className="app__body">
                  <div className="app__container">
                    <Dashboard current_user={this.props.current_user}/>
                    {this.handleCryptoListLoading()}
                  </div>
                </div>
              </Route>

              <Route exact path="/login">
                <LoginForm />
              </Route>

              <Route exact path="/signup">
                <SignUpForm />
              </Route>

              <Route exact path="/logout">
                <h1>Loguot</h1>
              </Route>
            
              
              <Route path="/coins/:coin_id" component={(routeInfo) => {
                const paramsCoinId = routeInfo.match.params.coin_id
                const foundCoin = this.props.current_user.coins.find(p=> p.coin_id === paramsCoinId)
                const foundCrypto = this.props.cryptoData.find(p=> p.symbol === paramsCoinId)
                const foundPortfolio = this.props.current_user.portfolios.find(p=> p.name === "Initial Portfolio")
                return <TradeCryptoHoldingDescription coins={foundCoin} current_user={this.props.current_user} crypto={foundCrypto} portfolio={foundPortfolio}/>
              </Route>

              <Route exact path="/trades">               {this.handleTradeCryptoHistoryListLoading()}
              </Route> 

              <Route exact path="/portfolio">
                {this.handleTradeCryptoListLoading()}
              </Route>            

              <Route path="/" render={() => <div><h1>Oops! That page doesn't exist.</h1></div>} />

            </Switch>
          </Router>
        </div>
      </>
    )
  }
}

const mapStateToProps = state => {
  return {
    cryptoData: state.crypto.cryptos,
    loading: state.crypto.loading,
    login: state.user.login,
    current_user: state.user.user,
    user_loading: state.user.loading
  }
}

const mapDispatchToProps = dispatch => {
  return {
    fetchCryptos: () => dispatch(fetchCryptos()),
    fetchLoggedInUser: () => dispatch(fetchLoggedInUser()),
    logOutUser: () => dispatch(logOutUser())
  }
}

export default connect(mapStateToProps, mapDispatchToProps)(App)
```
Actions: when fetching data from CoinGecko I use the `ADD_CRYPTOS` action:
```js
export const fetchCryptos = () => {
    return (dispatch) => {
        dispatch({ type: 'LOADING_CRYPTOS'})
        fetch('https://api.coingecko.com/api/v3/coins/markets?vs_currency=usd&ids=bitcoin%2C%20ethereum%2C%20binancecoin%2C%20tether%2C%20polkadot%2C%20cardano%2C%20ripple%2C%20litecoin%2C%20chainlink%2C%20bitcoin-cash%2C%20stellar%2C%20usd-coin%2C%20uniswap%2C%20dogecoin%2C%20wrapped-bitcoin%2C%20okb%2C%20aave%2C%20cosmos%2C%20nem%2C%20solana%20%20&order=market_cap_desc&per_page=100&page=1&sparkline=false&price_change_percentage=1h')
        .then(response => {
            return response.json()
        })
        .then(responseJSON => {
            dispatch({ type: 'ADD_CRYPTOS', cryptos: responseJSON})
        })
    }
}
```
Reducer: 
```js
const cryptosReducer = (state = {
    cryptos: [],
    loading:false
}, action) => {
    switch(action.type) {
        case 'LOADING_CRYPTOS':
            return {
                ...state,
                cryptos: [...state.cryptos],
                loading: true
            }
        case 'ADD_CRYPTOS':
            return {
                ...state,
                cryptos: action.cryptos,
                loading: false
            }
        default:
            return state
    }
}

export default cryptosReducer
``` 
I used a very similar approach to fetch user's data from my Rails API back end, you may check out front end repository here on [GitHub](https://github.com/drivera53/react-flatcoin) for full code.
Check out a quick [YouTube](https://www.youtube.com/watch?v=8Pp9b6Zprw8&t=1s) video of my application.
> I went through a lot of challenges when building up this App, but it gave me a clear understanding of React. Now I feel ready to keep on refining this project or start another from scratch and avoid those mistakes I made with this one.