Giter VIP home page Giter VIP logo

hoc-afternoon's Introduction

Project Summary

In this project, we'll be creating two of the same projects. One will be written as a higher order component (HOC) and the other will be written as a render prop. It is important to remember that these are two different patterns to accomplish the same task. In this project we will be building a currency converter that will convert a foreign currency to USD.

Step 1

Summary

In this step, we'll set up our file structure to keep things organized.

Instructions

  • Inside the src folder, create a Components folder.
  • Open src/Components. Create two new Files:
    • CurrencyConverter.js
    • CurrencyDisplay.js

Solution

File Structure
-- src
  -- Components
    -- CurrencyConverter.js
    -- CurrencyDisplay.js
  -- App.css
  -- App.js
  -- index.css
  -- index.js

Step 2

Summary

In this step, we'll create the skeleton for our Higher Order Component (HOC).

Instructions

  • Import React.
  • Create a function called withCurrency that will return a class component called Currency
  • The withCurrency function should take one paramter we will call BaseComponent.
Detailed Instructions

Remember, a Higher Order Component (HOC) is just a function that returns a new component. The naming convention of withNAMEofFUNCTION is common for HOC's.

We first want to create an arrow function called withCurrency that takes in one parameter, BaseComponent. The withCurrency function will return a new component that we will call Currency

const withCurrency = (BaseComponent) => (
  class Currency extends Component {...}
)

The BaseComponent parameter will be used to hold the template of a component we will pass in once we invoke the function.

Solution

src/Components/CurrencyConverter.js
import React, {Component} from 'react'

const withCurrency = (BaseComponent) => (
  class Currency extends Component {
    render(){
      return (
        // soon to be jsx
      )
    }
  }
)

Step 3

Summary

In this step, we will create the boilerplate for our currency converter. this will include a drop down and buttons to increment and decrement the amount to convert.

Instructions

  • Set intial state for this component. We will need three keys: currencyChosen : false, selected currency: 'Select Currency' and amount: 0.
const currencyData = [
	{ name: 'Japanese Yen', symbol: '¥', rate: 113.6, id: 0 },
	{ name: 'British Pound', symbol: '£', rate: 0.77, id: 1 },
	{ name: 'Canadian Dollar', symbol: 'CAD', rate: 1.32, id: 2 },
	{ name: 'Mexican Peso', symbol: 'Mex$', rate: 20.41, id: 3 },
	{ name: 'Swiss Franc', symbol: 'Fr.', rate: 1.01, id: 4 }
]
  • We will use the above array, currencyData, to map over and dynamically create options inside of a soon to be created select element.
  • Create a <select> element to hold the options created above along with a single default option with a value of 'Select Country'.
  • Create two <button> elements, one should have + as it's inner text and the other should be -.
  • Below the button's display the BaseComponent parameter like it is a React Component.
Detailed Instructions
  • Set some intial state for this component. We will need a currencyChosen which will default to false, selected currency which will default as 'Select Currency' (spelling and capitalization are important here) and finally an amount with the default of 0.
const currencyData = [
	{ name: 'Japanese Yen', symbol: '¥', rate: 113.6 },
	{ name: 'British Pound', symbol: '£', rate: 0.77 },
	{ name: 'Canadian Dollar', symbol: 'CAD', rate: 1.32 },
	{ name: 'Mexican Peso', symbol: 'Mex$', rate: 20.41 },
	{ name: 'Swiss Franc', symbol: 'Fr.', rate: 1.01 }
]
  • Copy the above currencyData array inside of thhe render() method but outside of the return inside of the Currency component.
  • Using .map(), create an <option> element for each item of the currencyData array. Each <option> element should have a key, and id and have the individual currency name as text. call the new array currencyOptions.
  • Create a container div <div>. Inside of the div create a <select> element.
  • Inside of the select create a single <option> element with a attribute of value='Select Currency' and 'Select Curreny' as the inner text. Below that, inside of {} display the currencyOptions.
  • Create a new <div> to hold buttons that will increment and decrement the currency amount.
  • Inside of the newly created div, create two <button> elements, one should have + as it's inner text and the other should be -. The button with the + should have a className of add and the button with the - should have a className of minus
  • Below the button's display the BaseComponent parameter like it is a React Component. The BaseComponent will have two props; one called currency which will use selectedCurrency from state as the index to select an option from the currencyData array and the other prop will be called amount which will be the value of amount on state.

Solution

src/Components/CurrencyConverter.js
import React, { Component } from 'react'

const withCurrency = (BaseComponent) =>
	class Currency extends Component {
		state = {
			currencyChosen: false,
			selectedCurrency: 'Select Currency',
			amount: 0
		}

		render() {
			const currencyData = [
				{ name: 'Japanese Yen', symbol: '¥', rate: 113.6 },
				{ name: 'British Pound', symbol: '£', rate: 0.77 },
				{ name: 'Canadian Dollar', symbol: 'CAD', rate: 1.32 },
				{ name: 'Mexican Peso', symbol: 'Mex$', rate: 20.41 },
				{ name: 'Swiss Franc', symbol: 'Fr.', rate: 1.01 }
			]
			const currencyOptions = currencyData.map((currency, index) => (
				<option key={index} value={index}>
					{currency.name}
				</option>
			))
			return (
				<div>
					<select value={this.state.selectedCurrency}>
						<option value='Select Currency'>Select Currency</option>
						{currencyOptions}
					</select>
					<div>
						<button className='add'>+</button>
						<button className='minus'>-</button>
					</div>
					<BaseComponent
						currency={currencyData[this.state.selectedCurrency]}
						amount={this.state.amount}
					/>
				</div>
			)
		}
	}

Step 4

Summary

In this step, we'll create three methods to help us handle user interactions. We will be using the auto-binding (public class field syntax for these methods).

Instructions

  • Using the public class field syntax, create a method that will increment the count of amount on state. Use setState via the callback function syntax. Call this method handleAmountIncrease.
  • Using the public class field syntax, create a method that will decrement the count of amount on state. Use setState via the callback function syntax. Call this method handleAmountDecrease.
  • Using the public class field syntax, create a method that will handle the users selection from the dropdown. You will need to store their selection in a variable we will call userValue. Call this method handleOptionSelect.
Detailed Instructions
  • Let's start off by creating a method that will handle the increase of the amount on state. We will be using the public class field syntax and using setState with a callback, rather than a object. The callback will take one parameter, prevState. This parameter gives us access to state without modifying it directly.
handleAmountIncrease = () => {
	this.setState((prevState) => {
		return {
			amount: (prevState.amount += 1)
		}
	})
}
  • Next we will create a method that will handle the decrease of the amount on state. We will be using the public class field syntax and using setState with a callback for this as well. The callback will take one parameter, prevState. This parameter gives us access to state without modifying it directly.
handleAmountDecrease = () => {
	this.setState((prevState) => {
		return {
			amount: (prevState.amount -= 1)
		}
	})
}
  • Finally we will create a method that will handle the user selection from the dropdown. We will be using the public class field syntax and using setState with a callback for this as well. The callback will take one parameter, prevState. This parameter gives us access to state without modifying it directly. This method will need an event passed into it. We will assign the value from evt.target.value to a variable we will call userValue. This setState won't need access to the prevState parameter but we will still use the callback syntax. Return a new object from setState that updates selectedCurrency and curencyChosen on state. The new value of selectedCurrency will be the userValue variable. The new value of currencyChosen will be a boolean. Using a ternary, determine if userValue is equal to 'Selected Currency' (punctuation matters here). If it does, set the value to false, otherwise set to true.
handleOptionSelect = (evt) => {
	const userValue = evt.target.value
	this.setState(() => {
		return {
			selectedCurrency: userValue,
			currencyChosen: userValue === 'Select Currency' ? false : true
		}
	})
}
  • Last step is to use these methods in the appropriate spots.
    • Using an onClick event listener, use the handleAmountIncrease method on the button with a + as the inner text.
    • Using an onClick event listener, use the handleAmountDecrease method on the button with a - as the inner text.
    • Using an onChange event listener, use the handleOptionSelect method on the select element.

Solution

src/Components/CurrencyConverter.js
import React, { Component } from 'react'

const withCurrency = (BaseComponent) =>
	class Currency extends Component {
		state = {
			currencyChosen: false,
			selectedCurrency: 'Select Currency',
			amount: 0
		}

		handleAmountIncrease = () => {
			this.setState((prevState) => {
				return {
					amount: prevState.amount + 1
				}
			})
		}

		handleAmountDecrease = () => {
			return (
				this.state.amount > 0 &&
				this.setState((prevState) => {
					return {
						amount: prevState.amount - 1
					}
				})
			)
		}

		handleOptionSelect = (evt) => {
			const userValue = evt.target.value
			this.setState(() => {
				return {
					selectedCurrency: userValue,
					currencyChosen: userValue === 'Select Currency' ? false : true
				}
			})
		}

		render() {
			const currencyData = [
				{ name: 'Japanese Yen', symbol: '¥', rate: 113.6 },
				{ name: 'British Pound', symbol: '£', rate: 0.77 },
				{ name: 'Canadian Dollar', symbol: 'CAD', rate: 1.32 },
				{ name: 'Mexican Peso', symbol: 'Mex$', rate: 20.41 },
				{ name: 'Swiss Franc', symbol: 'Fr.', rate: 1.01 }
			]
			const currencyOptions = currencyData.map((currency, index) => (
				<option key={index} value={index}>
					{currency.name}
				</option>
			))
			return (
				<div>
					<select
						value={this.state.selectedCurrency}
						onChange={this.handleOptionSelect}>
						<option value='Select Currency'>Select Currency</option>
						{currencyOptions}
					</select>
					<div>
						<button className='add' onClick={this.handleAmountIncrease}>
							+
						</button>
						<button className='minus' onClick={this.handleAmountDecrease}>
							-
						</button>
					</div>
					<BaseComponent
						currency={currencyData[this.state.selectedCurrency]}
						amount={this.state.amount}
					/>
				</div>
			)
		}
	}

Step 5

Summary

In this step, we'll invoke our function and build out a component to display the currency.

Instructions

  • At the bottom of the file, create a new variable called ExchangedCurrency which will hold the returned component from invoking withCurrency.
  • One thing to remember is that withCurrency required a paramater we called BaseComponent. Since we will be invoking the withCurrency function, we need to create a component to use.
  • Create a new file called CurrencyDisplay.js inside of our Components folder. This component will be a functional component, meaning it has no state. We will need to have props be one of our parameters. Think back to the previous step, what were the two props on BaseComponent? You can destructure them if you want. We will use data from props.currency and the amount from props.amount to display and convert our currency.
Detailed Instructions Pt 1
  • At the bottom of CurrencyConverter.js create a new constant called ExchangedCurrency. This variable will hold the result of invoking withCurrency. Because withCurrency requires a BaseComponent parameter, we will need to create that but first we will need to export our ExchangedCurrency variable.
  • Let's create a CurrencyDisplay.js file in Components/. This is where we'll display our converted currency. The component should be functional, take in one paramater called props and return some JSX. The JSX will be a <p></p> element. The p element should show the US Dollar amount, the name of the currency, the symbol and then the amount of the exchanged currency. Export the newly created component.
  • Back inside of CurrencyDisplay.js, import our CurrencyDisplay.js component and use it at the bottom of the file where we will be invoking the withCurrency function.

Solution

src/Components/CurrencyConverter.js
import React, { Component } from 'react'

const withCurrency = (BaseComponent) =>
	class Currency extends Component {
		// PREVIOUS STEPS
	}

const ExchangedCurrency = withCurrency()

export default ExchangedCurrency
src/Components/CurrencyDisplay.js
import React from 'react'

const CurrencyDisplay = (props) => (
	<p>
		US Dollar ${props.amount.toFixed(2)} - {props.currency.name}{' '}
		{props.currency.symbol}
		{(props.amount * props.currency.rate).toFixed(2)}
	</p>
)

export default CurrencyDisplay
src/Components/CurrencyConverter.js Pt 2
import React, { Component } from 'react'
import CurrencyDisplay from './CurrencyDisplay'

const withCurrency = (BaseComponent) =>
	class Currency extends Component {
		// PREVIOUS STEPS
	}

const ExchangedCurrency = withCurrency(CurrencyDisplay)

export default ExchangedCurrency

Step 6

Summary

In this step we need to import our newly created ExchangedCurrency component into App.js.

Instructions

  • Open App.js. Remove all of the existing code from Create React App and import ExchangedCurrency from ./Components/CurrencyConverter.js.
  • Render the component onto the screen but use a fragment rather than a div as it's parent container. Feel free to add an h2 element above to signal that this is a Higher Order Component. In the next project you will create a Render Prop version of this same project and the heading will help you know which one is which.

Solution

src/App.js
import React, { Component } from 'react'
import './App.css'

import CurrencyHOC from './Components/CurrencyConverter'

class App extends Component {
  render() {
    return (
      <>
        <h2>Higher Order Component</h2>
        <CurrencyHOC />
      </>
    )
  }
}

export default App

Step 7

Summary

You may have noticed by now that our project doesn't run yet and we are getting an error Cannot read name of undefined. Think for a moment about what may be causing this problem?

This is happening because we are trying to display our CurrencyDisplay.js component before we have anything on our currency prop. In this step we will conditionally render text if the user hasn't selected an option from the dropdown.

Instructions

  • Inside of CurrencyConverter.js, look to the return statement and find where we are rendering the BaseComponent. We will need to conditionally render this component only if a user has selected something from the dropdown. If the user has not, we will display the text Please Select Currency.
Detailed Instructions

Head down to the return of the Currency component inside of withCurrency. Here we will use a ternary to determine if our user has selected something from the dropdownn. Luckily on state we have a key of currencyChose which is a boolean. Use this to determine of we should display the BaseComponent or if we should display the text Please Select Currency.

Solution

src/Components/CurrencyConverter.js
import React, { Component } from 'react'
import CurrencyDisplay from './CurrencyDisplay'

const withCurrency = (BaseComponent) =>
	class Currency extends Component {
		state = {
			currencyChosen: false,
			selectedCurrency: 'Select Currency',
			amount: 0
		}

		handleOptionSelect = (evt) => {
			const userValue = evt.target.value
			this.setState(() => {
				return {
					selectedCurrency: userValue,
					currencyChosen: userValue === 'Select Currency' ? false : true
				}
			})
		}
    // OTHER STEPS
		render() {
      // OTHER STEPS
			return (
				<div>
          {/*OTHER STEPS*/}
					{this.state.currencyChosen ? (
						<BaseComponent
							currency={currencyData[this.state.selectedCurrency]}
							amount={this.state.amount}
						/>
					) : (
						<p>Please Select Currency</p>
					)}
				</div>
			)
		}
	}

const ExchangedCurrency = withCurrency(CurrencyDisplay)

export default ExchangedCurrency

Black Diamond ◆

Summary

This step is meant to stretch what you know and combine your knowledge. The first part of the black diamond is to make it so the user cannot go lower than 0. The second part of the challenge is to disable the buttons until an option from the dropdown has been selected.

The final challenge of the black diamond is to make it so every time a user selects a new currency it outputs a new currency display card rather than updating the same card each time.

Contributions

If you see a problem or a typo, please fork, make the necessary changes, and create a pull request so we can review your changes and merge them into the master repo and branch.

Copyright

© DevMountain LLC, 2017. Unauthorized use and/or duplication of this material without express and written permission from DevMountain, LLC is strictly prohibited. Excerpts and links may be used, provided that full and clear credit is given to DevMountain with appropriate and specific direction to the original content.

hoc-afternoon's People

Contributors

bryansmith33 avatar

Watchers

James Cloos avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.