DEV Community

Cover image for Understand components in React
Allister Mugaisi
Allister Mugaisi

Posted on

Understand components in React

Introduction

React is one of the biggest questions clients ask to a developer. Built by Facebook, react is one of the largely used UI library that helps with creation of beautiful web applications which require minimal effort and coding.

At the heart of react, is the concept of declarative programming. Whenever the state changes the interface automatically updates to reflect the change. This is supported by the performance and efficiency of the virtual D.O.M. Every time the UI needs to update, the changes are first performed in the virtual D.O.M. before reconciling it with the real D.O.M. This eliminates the need for expensive interaction with the real D.O.M. every time the UI needs to be updated.

In this article, I will mainly focus on components in react. Before we start, here is what you will learn in this post:

  • Defining components and their purpose.
  • Different types of components.
  • Key features of components.
  • Practical example of components in react.

What are components in react and their purpose?

Components are reusable features that you can compose together to create a user interface.

When you are on Facebook there are more features that you see and use. This includes individual posts, like and emotion buttons, create post section, notification section and so many other elements. Imagine all those as one huge monolithic code base with a ton of nested and intertwined features, it's not ideal.

On writing component based code means you can isolate them as features and develop them independently hence bringing scalability to equation. Modular code means debugging is exponentially easier than a monolithic code base.

Components helps you breakdown an interface into smaller manageable unit.

In fact, Facebook as of February 2019 uses more than 50,000 components in Facebook app, all built using react. Therefore everything that you build with react is a component.

Having said that, react uses a one way or unidirectional data flow order whereby a parent component will pass down data to the nested child components as props which act like inlet into a component.

This unidirectional data flow order makes it easy to understand, implement and debug common problems.

Different types of components

The are two types of components namely:

  • functional component.
  • class component.

Functional components are simple and lightweight components for when you need to render visual elements that depend mainly on props to render data.

A class component is the most fundamental form of component that you can build with react.

Key features of components

Functional components
  • simple functions.
  • Mainly responsible for the UI.
Class components
  • more feature rich.
  • maintain their own private data(state).
  • complex UI logic
  • provide lifecycle hooks.

Components can also contain other components e.g. app component which is the root component in react.

Initially, functional components do not implement local state and other features that you get in class components. But the recently introduced Hooks API allows you to implement local state and other features as in class components.

To understand this better let's dive into a practical example.

Prerequisites

You should have at least a basic understanding of fundamental programming concepts and some experience with introductory HTML/CSS/Javascript.

Make sure you have the following installed.

  • Text Editor (Atom) (or VS code/Sublime Text)

  • Latest version of Node.js (we’ll use npm, or “Node Package Manager”, to install dependencies—much like pip for Python or gems for Ruby)

  • Prettier (to seamlessly format our Javascript; in Atom, Packages → Prettier → Toggle Format on Save to automatically format on save)

Let's get started

Scaffold our client with create-react-app

We’ll be using create-react-app to set up our client. This will take care of a lot of heavy lifting for us (as opposed to creating a React project from scratch). I use this command to start all my React projects.

Set the current directory to wherever you want your project to live.

Now, create a know-zone directory and run npx create-react-app within it. The npx is a replacement for installing global packages. It encourages you to install packages locally, but still be able run them as if they were global, just with npx . You can also run npm i -g create-react-app to install create-react-app globally.

mkdir know-zone && cd know-zone && create-react-app .

Installing packages. This might take a couple of minutes.
Installing react, react-dom, and react-scripts...

run npm start to start our react-app

Our application will automatically start on localhost:3000 to view your React app. Depending on what version of create-react-app you are running, it may look a bit different.

localhost:3000

Let's clean up our React app by removing unnecessary files and code

  • Remove logo.svg in know-zone/src
  • Take out the import of logo.svg in App.js
  • Remove all the CSS in App.css (we’ll keep the import in App.js in case you want to add your own global CSS here)
  • Clear out the content in the main div in App.js and replace it with an <h1> for now.

You should have no errors and your App.js should look like this at this point.

import React, { Component } from "react";
import "./App.css";
class App extends Component {
  render() {
    return (
      <div className="App">
        <h1>Hello world.</h1>
      </div>
    );
  }
}
export default App;

Navigate back to localhost:3000 and you should see this.

hello world

creating our static components

In our src directory, let’s create a folder for our components and create a KnowZone.js QuestionBox.js and Result.js files. The KnowZone.js file will host most of our app's logic before importing it to the App.js file.

➜  src mkdir components && cd components && touch KnowZone.js QuestionBox.js Result.js

KnowZone.js

Whenever you are building a react component, you need to import a react library first. Additionally, we are also importing the component class because we will be building a class component in this file which is sub-classed from component class.

A side note on destructuring in React: const { errors } = this.state; is the same as doing const errors = this.state.errors;. It is less verbose and looks cleaner in my opinion.

Let’s put the following code in our KnowZone.js.


import React, { Component } from "react";
import quizService from "./quizService";
import QuestionBox from "./QuestionBox";
import Result from "./Result";
import "./assets/style.css";

class KnowZone extends Component {
  constructor(props) {
    super(props);
    this.state = {
      questionBank: [],
      score: 0,
      responses: 0,
    };
  }

  getQuestions = () => {
    quizService().then((question) => {
      this.setState({
        questionBank: question,
      });
    });
  };

  computeAnswer = (answer, correctAnswer) => {
    if (answer === correctAnswer) {
      this.setState({
        score: this.state.score + 1,
      });
    }
    this.setState({
      responses: this.state.responses < 5 ? this.state.responses + 1 : 5,
    });
  };

  playAgain = () => {
    this.getQuestions();
    this.setState({
      score: 0,
      responses: 0,
    });
  };

  componentDidMount() {
    this.getQuestions();
  }

  render() {
    return (
      <div className="container">
        <div className="title">Know-zone</div>
        {this.state.questionBank.length > 0 &&
          this.state.responses < 5 &&
          this.state.questionBank.map(
            ({ question, answers, correct, questionId }) => (
              <QuestionBox
                question={question}
                options={answers}
                key={questionId}
                selected={(answer) => this.computeAnswer(answer, correct)}
              />
            )
          )}
        {this.state.responses === 5 ? (
          <Result score={this.state.score} playAgain={this.playAgain} />
        ) : null}
      </div>
    );
  }
}

export default KnowZone;



You will realize, we've imported a stylesheet file in the KnowZone.js file. This may look a bit weird, but importing a stylesheet file in our javascript file is a standard practice since our webpack built-in system supports it and will automatically create the necessary files for distribution at the end.

Furthermore, notice the use of className instead of class. The syntax is called jsx. JSX just provides syntactic sugar for the React.createElement(component, props, ...children) function. The JSX code:

<MyButton color="blue" shadowSize={2}>
  Click Me
</MyButton>

compiles into:

React.createElement(
  MyButton,
  {color: 'blue', shadowSize: 2},
  'Click Me'
)

JSX will keep the code elegant, simple and readable. To read more on JSX syntax here is the link JSX in Depth-React

In our component directory, let’s create two folders for our style.css file and quizService api.

➜  components mkdir assets quizService && cd assets && touch style.css && cd ../ && cd quizService && touch index.js 

style.css

Let's place the following in our style.css file.

@import url("https://fonts.googleapis.com/css?family=Raleway");

* {
  font-family: "Raleway", sans-serif;
}

.container {
  position: relative;
  width: 1000px;
  height: 100vh;
  border: 1px solid rgba(0, 0, 0, 0.2);
  box-shadow: 0px 2px 10px rgba(0, 0, 0, 0.1);
  border-radius: 10px;
  padding: 10px;
  box-sizing: border-box;
  top: 30px;
  bottom: 0px;
  left: 0px;
  right: 0px;
  margin: auto;
}

.title {
  position: relative;
  background-color: rgba(0, 0, 0, 1);
  color: rgb(255, 255, 255);
  padding: 15px;
  font-size: 1.8em;
  border-radius: 5px;
}

.question {
  font-size: 1em;
  font-weight: bold;
  margin-bottom: 5px;
}

.questionBox {
  position: relative;
  border: 1px solid rgba(0, 0, 0, 0.2);
  margin-top: 5px;
  padding: 15px;
  box-sizing: border-box;
  background-color: rgb(250, 250, 250);
}

.answerBtn {
  position: relative;
  padding: 5px;
  background-color: #2680eb;
  display: inline-block;
  margin: 10px;
  outline: none;
  border: none;
  font-size: 0.8em;
  color: rgb(255, 255, 255);
  font-weight: bold;
  border-radius: 4px;
  cursor: pointer;
}

.score-board {
  position: relative;
  height: 700px;
}

.score {
  position: absolute;
  width: 1024px;
  font-size: 2em;
  top: 150px;
  text-align: center;
  color: #2680eb;
}

.playBtn {
  position: absolute;
  font-size: 1.4em;
  padding: 10px;
  top: 250px;
  background-color: rgb(46, 182, 46);
  color: rgb(255, 255, 255);
  width: 150px;
  border-radius: 15px;
  left: 0px;
  right: 0px;
  margin: auto;
  outline: none;
  cursor: pointer;
}


quizService api

The quizService api contains a batch of questions each with four answers, one correct answer and a unique id. What we are exporting out of this service, is actually a function that randomly picks five questions. We later return the questions as promise.

Place the following code in index.js file of the quizService directory.

const qBank = [
  {
    question:
      "Virgin Trains, Virgin Atlantic and Virgin Racing, are all companies owned by which famous entrepreneur?   ",
    answers: ["Richard Branson", "Alan Sugar", "Donald Trump", "Bill Gates"],
    correct: "Richard Branson",
    questionId: "099099",
  },
  {
    question:
      'Where is the train station "Llanfair­pwllgwyngyll­gogery­chwyrn­drobwll­llan­tysilio­gogo­goch"?',
    answers: ["Wales", "Moldova", "Czech Republic", "Denmark"],
    correct: "Wales",
    questionId: "183452",
  },
  {
    question:
      "Which company did Valve cooperate with in the creation of the Vive?",
    answers: ["HTC", "Oculus", "Google", "Razer"],
    correct: "HTC",
    questionId: "267908",
  },
  {
    question: "What's the name of Batman's  parents?",
    answers: [
      "Thomas & Martha",
      "Joey & Jackie",
      "Jason & Sarah",
      "Todd & Mira",
    ],
    correct: "Thomas & Martha",
    questionId: "333247",
  },
  {
    question: "What is the most common surname Wales?",
    answers: ["Jones", "Williams", "Davies", "Evans"],
    correct: "Jones",
    questionId: "496293",
  },
  {
    question:
      "What was the name of the WWF professional wrestling tag team made up of the wrestlers Ax and Smash?",
    answers: [
      "Demolition",
      "The Dream Team",
      "The Bushwhackers",
      "The British Bulldogs",
    ],
    correct: "Demolition",
    questionId: "588909",
  },
  {
    question:
      'What name represents the letter "M" in the NATO phonetic alphabet?',
    answers: ["Mike", "Matthew", "Mark", "Max"],
    correct: "Mike",
    questionId: "648452",
  },
  {
    question: "What is the first book of the Old Testament?",
    answers: ["Genesis", "Exodus", "Leviticus", "Numbers"],
    correct: "Genesis",
    questionId: "786649",
  },
  {
    question:
      "In the video-game franchise Kingdom Hearts, the main protagonist, carries a weapon with what shape?",
    answers: ["Key", "Sword", "Pen", "Cellphone"],
    correct: "Key",
    questionId: "839754",
  },
  {
    question:
      "Which best selling toy of 1983 caused hysteria, resulting in riots breaking out in stores?",
    answers: [
      "Cabbage Patch Kids",
      "Transformers",
      "Care Bears",
      "Rubik’s Cube",
    ],
    correct: "Cabbage Patch Kids",
    questionId: "98390",
  },
  {
    question: "What does a funambulist walk on?",
    answers: ["A Tight Rope", "Broken Glass", "Balls", "The Moon"],
    correct: "A Tight Rope",
    questionId: "1071006",
  },
  {
    question: "In past times, what would a gentleman keep in his fob pocket?",
    answers: ["Watch", "Money", "Keys", "Notebook"],
    correct: "Watch",
    questionId: "1174154",
  },
  {
    question: "Area 51 is located in which US state?",
    answers: ["Nevada", "Arizona", "New Mexico", "Utah"],
    correct: "Nevada",
    questionId: "1226535",
  },
  {
    question: "How would one say goodbye in Spanish?",
    answers: ["Adiós", " Hola", "Au Revoir", "Salir"],
    correct: "Adiós",
    questionId: "1310938",
  },
  {
    question: "What is the largest organ of the human body?",
    answers: ["Skin", "Heart", "large Intestine", "Liver"],
    correct: "Skin",
    questionId: "1436873",
  },
  {
    question: "Which sign of the zodiac is represented by the Crab?",
    answers: ["Cancer", "Libra", "Virgo", "Sagittarius"],
    correct: "Cancer",
    questionId: "1515110",
  },
  {
    question: "On a dartboard, what number is directly opposite No. 1?",
    answers: ["19", "20", "12", "15"],
    correct: "19",
    questionId: "1642728",
  },
  {
    question:
      "What does the 'S' stand for in the abbreviation SIM, as in SIM card? ",
    answers: ["Subscriber", "Single", "Secure", "Solid"],
    correct: "Subscriber",
    questionId: "1747256",
  },
  {
    question:
      "What word represents the letter 'T' in the NATO phonetic alphabet?",
    answers: ["Tango", "Target", "Taxi", "Turkey"],
    correct: "Tango",
    questionId: "1822532",
  },
  {
    question: "Which American president appears on a one dollar bill?",
    answers: [
      "George Washington",
      "Thomas Jefferson",
      "Abraham Lincoln",
      "Benjamin Franklin",
    ],
    correct: "George Washington",
    questionId: "195075",
  },
  {
    question: 'What is "dabbing"?',
    answers: ["A dance", "A medical procedure", "A sport", "A language"],
    correct: "A dance",
    questionId: "2019778",
  },
  {
    question: "What is the name of the Jewish New Year?",
    answers: ["Rosh Hashanah", "Elul", "New Year", "Succoss"],
    correct: "Rosh Hashanah",
    questionId: "2134343",
  },
  {
    question: "Which one of the following rhythm games was made by Harmonix?",
    answers: [
      "Rock Band",
      "Meat Beat Mania",
      "Guitar Hero Live",
      "Dance Dance Revolution",
    ],
    correct: "Rock Band",
    questionId: "2210799",
  },
  {
    question:
      "What type of animal was Harambe, who was shot after a child fell into it's enclosure at the Cincinnati Zoo?",
    answers: ["Gorilla", "Tiger", "Panda", "Crocodile"],
    correct: "Gorilla",
    questionId: "2379831",
  },
  {
    question: "Red Vines is a brand of what type of candy?",
    answers: ["Licorice", "Lollipop", "Chocolate", "Bubblegum"],
    correct: "Licorice",
    questionId: "2426418",
  },
  {
    question: "What is the nickname of the US state of California?",
    answers: ["Golden State", "Sunshine State", "Bay State", "Treasure State"],
    correct: "Golden State",
    questionId: "2510086",
  },
  {
    question: "What is on display in the Madame Tussaud's museum in London?",
    answers: [
      "Wax sculptures",
      "Designer clothing",
      "Unreleased film reels",
      "Vintage cars",
    ],
    correct: "Wax sculptures",
    questionId: "2685745",
  },
  {
    question:
      "What was the nickname given to the Hughes H-4 Hercules, a heavy transport flying boat which achieved flight in 1947?",
    answers: ["Spruce Goose", "Noah's Ark", "Fat Man", "Trojan Horse"],
    correct: "Spruce Goose",
    questionId: "2796884",
  },
  {
    question: "Which of these colours is NOT featured in the logo for Google?",
    answers: ["Pink", "Yellow", "Blue", "Green"],
    correct: "Pink",
    questionId: "2838900",
  },
  {
    question: 'What is the French word for "hat"?',
    answers: ["Chapeau", "Bonnet", " Écharpe", " Casque"],
    correct: "Chapeau",
    questionId: "298060",
  },
  {
    question: "Five dollars is worth how many nickles?",
    answers: ["100", "50", "25", "69"],
    correct: "100",
    questionId: "3096579",
  },
  {
    question: "Who is depicted on the US hundred dollar bill?",
    answers: [
      "Benjamin Franklin",
      "George Washington",
      "Abraham Lincoln",
      "Thomas Jefferson",
    ],
    correct: "Benjamin Franklin",
    questionId: "3182461",
  },
  {
    question: "What do the letters in the GMT time zone stand for?",
    answers: [
      "Greenwich Mean Time",
      "Global Meridian Time",
      "General Median Time",
      "Glasgow Man Time",
    ],
    correct: "Greenwich Mean Time",
    questionId: "3239112",
  },
  {
    question: "Which one of these is not a typical European sword design?",
    answers: ["Scimitar", "Falchion", "Ulfberht", "Flamberge"],
    correct: "Scimitar",
    questionId: "3318503",
  },
  {
    question:
      'According to Sherlock Holmes, "If you eliminate the impossible, whatever remains, however improbable, must be the..."',
    answers: ["Truth", "Answer", "Cause", "Source"],
    correct: "Truth",
    questionId: "3410327",
  },
  {
    question: "What is the name of Poland in Polish?",
    answers: ["Polska", "Pupcia", "Polszka", "Póland"],
    correct: "Polska",
    questionId: "3542596",
  },
  {
    question: "The New York Times slogan is, “All the News That’s Fit to…”",
    answers: ["Print", "Digest", "Look", "Read"],
    correct: "Print",
    questionId: "3667517",
  },
  {
    question: "What do the letters of the fast food chain KFC stand for?",
    answers: [
      "Kentucky Fried Chicken",
      "Kentucky Fresh Cheese",
      "Kibbled Freaky Cow",
      "Kiwi Food Cut",
    ],
    correct: "Kentucky Fried Chicken",
    questionId: "3791672",
  },
  {
    question: "Which restaurant's mascot is a clown?",
    answers: ["McDonald's", "Whataburger", "Burger King", "Sonic"],
    correct: "McDonald's",
    questionId: "3893585",
  },
  {
    question: 'What color is the "Ex" in FedEx Ground?',
    answers: ["Green", "Red", "Light Blue", "Orange"],
    correct: "Green",
    questionId: "3913430",
  },
  {
    question: "How tall is the Burj Khalifa?",
    answers: ["2,722 ft", "2,717 ft", "2,546 ft", "3,024 ft"],
    correct: "2,722 ft",
    questionId: "4049121",
  },
  {
    question:
      "Which of the following card games revolves around numbers and basic math?",
    answers: ["Uno", "Go Fish", "Twister", "Munchkin"],
    correct: "Uno",
    questionId: "4150746",
  },
  {
    question:
      "What machine element is located in the center of fidget spinners?",
    answers: ["Bearings", "Axles", "Gears", "Belts"],
    correct: "Bearings",
    questionId: "4235063",
  },
  {
    question: "Which sign of the zodiac comes between Virgo and Scorpio?",
    answers: ["Libra", "Gemini", "Taurus", "Capricorn"],
    correct: "Libra",
    questionId: "4321002",
  },
  {
    question: "Which of the following presidents is not on Mount Rushmore?",
    answers: [
      "John F. Kennedy",
      "Theodore Roosevelt",
      "Abraham Lincoln",
      "Thomas Jefferson",
    ],
    correct: "John F. Kennedy",
    questionId: "4442286",
  },
  {
    question: "What is Tasmania?",
    answers: [
      "An Australian State",
      "A flavor of Ben and Jerry's ice-cream",
      "A Psychological Disorder",
      "The Name of a Warner Brothers Cartoon Character",
    ],
    correct: "An Australian State",
    questionId: "4564430",
  },
  {
    question: "What company developed the vocaloid Hatsune Miku?",
    answers: ["Crypton Future Media", "Sega", "Sony", "Yamaha Corporation"],
    correct: "Crypton Future Media",
    questionId: "4630606",
  },
  {
    question:
      "Which country, not including Japan, has the most people of japanese decent?",
    answers: ["Brazil", "China", "South Korea", "United States of America"],
    correct: "Brazil",
    questionId: "4795960",
  },
  {
    question: "Which candy is NOT made by Mars?",
    answers: ["Almond Joy", "M&M's", "Twix", "Snickers"],
    correct: "Almond Joy",
    questionId: "4811162",
  },
  {
    question: "In which fast food chain can you order a Jamocha Shake?",
    answers: ["Arby's", "McDonald's", "Burger King", "Wendy's"],
    correct: "Arby's",
    questionId: "4982981",
  },
];

export default (n = 5) =>
  Promise.resolve(qBank.sort(() => 0.5 - Math.random()).slice(0, n));

Remember in our KnowZone.js file, we used componentDidMount() lifecycle method to bring in a set of question objects from our api and populate a state variable with this data. We then rendered the question text from this data in our <QuestionBox /> component.

The componentDidMount() lifecycle method is a hook that gets invoked right after a React component has been mounted i.e. after the first render() lifecycle. It instantiates a component being created and inserts it into the D.O.M.

It is important to note, any changes made to the questionBank array would automatically signal an update causing the component to re-render with the updated data.

Now let's create our <QuestionBox /> component to display a set of choices button, so that our users can select the correct answer.

The <QuestionBox /> component is an opportunity to showcase how a functional component works in React.

Functional component unlike class component, do not implement state management features. This is ideal because you won't want state management and lifecycle features in components that only have to display data and provide simple interaction features.

Having said that, should you need to incorporate state in a functional component, react offers you a plugin api called hooks.

QuestionBox.js

Place the following in QuestionBox.js file.

import React, { useState } from "react";

const QuestionBox = ({ question, options, selected }) => {
  const [answer, setAnswer] = useState(options);
  return (
    <div className="questionBox">
      <div className="question">{question}</div>
      {answer.map((text, index) => (
        <button
          key={index}
          className="answerBtn"
          onClick={() => {
            setAnswer([text]);
            selected(text);
          }}
        >
          {text}
        </button>
      ))}
    </div>
  );
};

export default QuestionBox;


A functional component with just one state variable is a more efficient idea. However, feel free to implement a class component in the QuestionBox.js file.

Now that we are using state to manage our score and the number of responses, we can build another component to display the results and also offer the user to play the game again.

Result.js

Place the following code in Result.js file

import React from "react";

const Result = ({ score, playAgain }) => (
  <div className="score-board">
    <div className="score">This is what you have scored {score}</div>
    <button className="playBtn" onClick={playAgain}>
      Play again!
    </button>
  </div>
);

export default Result;

Now our know-zone app is complete. Your app should look like this

know-zone app

know-zone app

Conclusion

So far, we've been using our development server to develop our app. But, this is not what you are going to upload on the server. We need to create our production build for our app. This process creates compacted bundles, so you have minimum files to deploy. It correctly bundles React in production mode and optimizes the build for the best performance.

yarn build or npm build

Builds the app for production to the build folder. Use the command npm run build if you are using npm or yarn run build for yarn

To deploy your app, just upload everything inside the build folder to your server and your app is ready for use.

See the section about deployment for more information.

I'd love to hear from you. Thanks for reading, I appreciate it!

Top comments (0)