March 3, 2018

/

React Crypto Stock Compare – Pt. 4: Using Props

React Crypto Stock Compare – Pt. 4: Using Props

This is to part 4 of a multi-part React tutorial that will teach you the basics of React while building a simple app to compare crypto-currency prices to stock prices. You can find the code to work along here and the starting point for this step can be found in the branch splitting-components by running git checkout splitting-components

In this step I’m going to give you a crash course on using one of the key parts of React, props. Its very well said in the React Docs “Conceptually, components are like JavaScript functions. They accept arbitrary inputs (called “props”) and return React elements describing what should appear on the screen.”

What this means is that when you would like to pass some data to a component you use props. These look just like regular HTML attributes except they are camelCased not snake-cased. This is true of all of the regular HTML attributes too.

Here is an example in functional component:

const MyFunctionalComponentWithProps = (props) => {
  return {
    <div>{props.myValue}</div>
  }
}

Here is an example in a class component

class MyClassComponentWithProps {
  render(){
    return {
      <div>{this.props.myValue}</div>
    }
  }
}

As you can see when you use a functional component props are passed as an argument to that function. When you use a class component props are available as this.props. To pass props you would pass it as an attribute on the component from it’s parent component.

class MyParentComponent {
  render(){
    return {
      <MyClassComponentWithProps myValue="Some value" />
    }
  }
}

Or if the value itself is a variable:

class MyParentComponent {
  render(){
    const myValue = "Some value";
    return {
      <MyClassComponentWithProps myValue={myValue} />
    }
  }
}

That is all you really need to know about props. One nice thing you can do with the functional component though is use destructuring assignment to get access to each prop in the function definition like this:

const MyFunctionalComponentWithProps = ({ myProp1, myProp2 }) => {
  return {
    <div>{myProp1}, {myProp2 }</div>
  }
}

Instead of:

const MyFunctionalComponentWithProps = (props) => {
  return {
    <div>{props.myProp1}, {props.myProp2}</div>
  }
}

Coding Along

If you haven’t been following along and would like to you can clone the repository and get the starting code for this step by running:

git clone https://github.com/sethreidnz/crypto-stock-compare
git checkout splitting-components

Using Props to pass data to the tables

The first thing I want to point out is that in the last step we created two components MicrosoftRow and EthereumRow. This was convenient but not ideal since they are basically the same thing apart from the data, and this means a lot of repeated code. What we should do is think of a way to generalise the components.

First a component called PriceRow in src/component/PriceRow:

import React from "react";

export const PriceRow = ({ dayData }) => {
  const arrowClass = `arrow-${dayData.change > 0 ? "up" : "down"}`;
  return (
    <tr key={dayData.date}>
      <td>{dayData.date}</td>
      <td>{dayData.open}</td>
      <td>{dayData.low}</td>
      <td>{dayData.high}</td>
      <td>
        <span className={arrowClass} />
        {dayData.change}%
      </td>
    </tr>
  )
};

I am using a functional component and I’m destructuring the props to get our the prop dayData, but otherwise this is very similar to the two row components we have already. This means we can just pass this component the data so we don’t need two different components to handle two sets of data.

Now we can replace the two components <EthereumRow /> and <MicrosoftRow /> in the App component with the <PriceRow /> component using a map function. Replace the whole of App.js with the following:

import React, { Component } from "react";
import "./App.css";

import { getEthereumData } from "./api";
import { getMicrosoftData } from "./api";

import { PriceRow } from "./components/PriceRow";

const ethereumData = getEthereumData();
const microsoftData = getMicrosoftData();

class App extends Component {
  render() {
    return (
      <div className="crypto-stock-compare">
        <h1>Crypto Stock Compare</h1>
        <section className="value-table">
          <h2>Ethereum</h2>
          <table>
            <thead>
              <tr>
                <th>Date</th>
                <th>Open</th>
                <th>Low</th>
                <th>High</th>
                <th>Change</th>
              </tr>
            </thead>
            <tbody>
              {ethereumData.map(dayData => 
                <PriceRow dayData={dayData} />
              )}             
            </tbody>
          </table>
        </section>
        <section className="value-table">
          <h2>Microsoft</h2>
          <table>
            <thead>
              <tr>
                <th>Date</th>
                <th>Open</th>
                <th>Low</th>
                <th>High</th>
                <th>Change</th>
              </tr>
            </thead>
            <tbody>             
              {microsoftData.map(dayData => 
                <PriceRow dayData={dayData} />
              )}
            </tbody>
          </table>
        </section>
      </div>
    );
  }
}

export default App;

This is much nicer now but I think we can do one better. The rest of the tables are exactly the same too, so we let’s create a new component called PriceTable in src/component/PriceTable.js:

import React from "react";
import { PriceRow } from "./PriceRow";

export const PriceTable = ({ priceData }) => (
  <table>
    <thead>
      <tr>
        <th>Date</th>
        <th>Open</th>
        <th>Low</th>
        <th>High</th>
        <th>Change</th>
      </tr>
    </thead>
    <tbody>
      {priceData.map(dayData => (
        <PriceRow dayData={dayData} key={dayData.date} />
      ))}
    </tbody>
  </table>
);

Remove the line that is importing the PriceRow in src/App.js:

import { PriceRow } from "./components/PriceRow";

Add a new line to import the PriceTable component:

import { PriceTable } from "./components/PriceTable";

Then we can whittle down the App.js component even further:

class App extends Component {
  render() {
    return (
      <div className="crypto-stock-compare">
        <h1>Crypto Stock Compare</h1>
        <section className="value-table">
          <h2>Ethereum</h2>
          <PriceTable priceData={ethereumData} />
        </section>
        <section className="value-table">
          <h2>Microsoft</h2>
          <PriceTable priceData={microsoftData} />
        </section>
      </div>
    );
  }
}

This sticks to what I said in the previous section that we should try to split components up to make sure that each component only has only one (or few) concerns. In this case we have the App component that is basically just a layout and styling shell while the PriceTable and the PriceRow take care of their own rendering logic and markup.

If you got lost or want to make sure you did it right you can have a look in the branch in Github or run:

git checkout using-props

In the next section we are going to introduce the idea of doing asynchronous requests to an API like you would in almost any real application in Using state to manage fetching data from an API

Next Step

Tags:

Seth Reid

/

React Crypto Stock Compare – Pt. 4: Using Props