How to Create Custom React Components

Wed May 31, 2017 - 1100 Words

Learn to create your first custom React components in this tutorial. We will be building on the starter project from last week’s tutorial, building a musical chord charting application.

Goals:

  • Create a general layout for the application.
  • Build a component to render ChordPro to an HTML table.

One of the big things that we need to be able to do to render out chord charts is to have them spaced properly and like it or not a table will work great for that. Through today’s tutorial we will get rid of our “Hello, World” component contents and create a few new components. We’ll represent the structure of our application page (header, content, footer, etc) and also start working on the main feature of the application. By the time we’re done we’ll have integrated ChordSheetJS and will be parsing ChordPro to render a chord chart.

Pulling in a New Dependency

We know we need ChordSheetJS so we might as well pull in that dependency now. We’ll be using NPM for this:

$ npm install chordsheetjs --save

That will pull in the module from NPM and add it to our package.json file so that we can track it as a dependency. Now we will be ableto import this package into our components.

Structuring our Application with Components

We have our the main entrypoint into our application as the App component, and we’re going to keep it that way, but we we’re going to instead call on other components to handle the bulk of the work. Let’s write our our ideal App component now and build out the rest of components from there.

src/app.js

import React, { Component } from 'react';
import Header from './components/Header';
import Footer from './components/Footer';
import ChordEditor from './components/ChordEditor';

class App extends Component {
  render() {
    return (
      <div className="wrapper">
        <Header />
        <div className="workspace">
          <ChordEditor />
        </div>
        <Footer />
      </div>
    );
  }
}

export default App;

We’ve imported 3 new components that we have yet to write, but looking at the import lines you can see that we’re wanting them to be nested under a components directory. Let’s create that now:

$ mkdir src/components

With these components imported we’re free to use them, but remember that we can only return a single element from our component’s render function. Because of this limitation we’re going to wrap everything in div with the class of wrapper. From here we’re utilizing our Header, ChordEditor, and Footer components. We can reference our components in JSX as though they are custom HTML elements.

Note: You do need to self close these tags using a />. JSX requires that all tags be explicitly closed.

Now that we’ve broken the application by using components that don’t exist it’s time to create those.

The ChordEditor component will be doing the real work in our application, so we’ll save that for last. Let’s create the structural Header and Footer components now.

$ touch src/components/{Header,Footer}.js

These components are going to be mostly placeholder for right now, but I promise they will contain actual content later.

src/components/Header.js

import React, { Component } from 'react';

class Header extends Component {
  render() {
    return (
      <header>
        <h1>Chords</h1>
      </header>
    );
  }
}

export default Header;

And for the Footer we’ll make it dynamically update the copyright year.

src/components/Footer.js

import React, { Component } from 'react';

class Footer extends Component {
  constructor(props) {
    super(props);
    this.state = { year: new Date().getFullYear() };
  }

  render() {
    return (
      <footer>
        <ul className="site-links">
          <li>
            &copy; {this.state.year} CoderJourney
          </li>
        </ul>
      </footer>
    );
  }
}

export default Footer;

We’re utilizing another feature of a React component here by defining the constructor method. This is the method that gets called when a component is created and to prevent issue with the default implementation we need to make sure to call super(props) as the first thing. We’re setting up the component’s state so that we can display the year without ever needing to change it. This value could likely be a prop since it won’t change. You can see that we utilize this.state.year in the JSX output by putting it in {...}.

Parsing ChordPro Notation

As you’d probably expect, the ChordEditor component will be a little more complicated than the others. We will need to tie into events on the elements within our component in order to dynamically update the screen when the user makes changes and we’ll be transforming the content along the way. Here’s the final component (for this tutorial at least):

src/components/ChordEditor.js

import React, { Component } from 'react';
import ChordSheetJS from 'chordsheetjs';

class ChordEditor extends Component {
  constructor(props) {
    super(props);
    this.handleChange = this.handleChange.bind(this);
    this.state = { value: 'Type some lyrics here' };
  }

  handleChange(e) {
    this.setState({value: e.target.value});
  }

  getChordMarkup() {
    var formatter = new ChordSheetJS.HtmlFormatter(),
      parser = new ChordSheetJS.ChordProParser(),
      song = parser.parse(this.state.value);

    return { __html: formatter.format(song) };
  }

  render() {
    return (
      <div className="chord-editor">
        <div className="panel">
          <h3>Input</h3>
          <textarea
            style={{width: "100%", height: "100%"}}
            onChange={this.handleChange}
            defaultValue={this.state.value} />
        </div>
        <div className="panel">
          <h3>Output</h3>
          <div
            style={{width: "100%", height: "100%", fontFamily: "monospace"}}
            className="chord-output"
            dangerouslySetInnerHTML={this.getChordMarkup()} />
        </div>
      </div>
    );
  }
}

export default ChordEditor;

From the top, we’re importing ChordSheetJS. In the constructor for this component we’re doing much of the same things that we did in the Footer portion, but we’re also using .handleChange.bind(this) to ensure that this inside of the handleChange method always points to the component itself.

Cleaning Up Styles (Slightly)

The application looks pretty rough at the moment, so we need to sprinkle in a little CSS. For the time being we’re just going to create a style block at the top of our index.html to hold the styles because we’re mostly just going to be changing up spacing.

public/index.html

<!-- Only showing new style tag in the <head/> element -->
<style>
  body, html, #root {
    height: 100%;
    width: 100%;
    margin: 0;
    padding: 0;
  }
  .chord-editor {
    display: flex;
    flex-direction: row;
    min-height: 50vh;
  }
  .panel {
    flex: 1;
    margin: 5px;
    display: flex;
    flex-direction: column;
  }
  .chord-output {
    border-spacing: 0;
    border-collapse: collapse;
  }
  .chord-output td {
    padding: 0;
  }
  .chord-output table {
    margin: 10px 0;
  }
</style>

Recap

Throughout this tutorial you were hopefully able to get a better grasp on how you can use JSX and React components to incrementally build up an application. We’re now well on our way to creating a useful application for song writers.