Components are one of the pieces that make React, well, React! They’re one of the primary ways you have for defining the visuals and interactions that make up what people see when they use your app. Let’s say Figure 3.1 shows what your finished app looks like.
Figure 3.1 Your hypothetical finished app.
This is the finished sausage. During development, viewed through the lens of a React project, things might look a little less appealing. Almost every part of this app’s visuals would be wrapped inside a self-contained module known as a component. To highlight what “almost every” means here, take a look at the diagram in Figure 3.2.
Figure 3.2 Diagrammatic representation of the app components.
Each dotted line represents an individual component that is responsible for both what you see and any interactions that it is responsible for. Don’t let this scare you. While this looks really complicated, you will soon see that it will start to make a whole lot of sense once you’ve had a chance to play with components and some of the awesome things they do—or, at least, try really hard to do.
In JavaScript, you have functions that enable you to make your code a bit cleaner and more reusable. Now, there’s reason we’re taking some time to look at functions, and it isn’t to annoy you! Conceptually, functions share a lot of surface area with React components, and the easiest way to understand what components do is to take a quick look at functions first.
In a terrible world where functions don’t exist, you might have some code that looks as follows:
In a really chill world that involves functions, you can condense all that duplicated text into something simple, like the following:
Our getDistance
function removes all the duplicated code you saw earlier, and it takes speed and time as arguments to allow you to customize the calculation that gets returned.
To call this function, all you have to do is this:
Doesn’t this look nicer? Functions provide another great value, too. Your functions (such as the alert inside getDistance
) can call other functions as part of their running. Take a look at using a formatDistance
function to change what getDistance
returns:
This capability to have functions call other functions enables us to cleanly separate what functions do. You don’t need to have one monolithic function that does everything under the sun; you can distribute functionality across many functions that are specialized for a particular type of task.
Best of all, after you make changes to how your functions work, you don’t have to do anything extra to see the results of those changes. If the function signature didn’t change, any existing calls to that function will just magically work and automatically pick up any new changes you made to the function itself.
In a nutshell, functions are awesome. I know that. You know that. That’s why all of the code we write has them all over the place.
I don’t think anybody will disagree with the good functions bring to the table. They really make it possible to structure the code for your apps in a sane way. That same level of care we use in writing our code isn’t always possible when it comes to writing our UIs. For various technical and nontechnical reasons, we’ve always tolerated a certain level of sloppiness with how we typically work with our UI elements.
That’s a pretty controversial statement, so let me highlight what I mean by looking at some examples. Let’s go back and look at the render
method we used in the previous chapter:
Onscreen, you see the word Batman printed in giant letters, thanks to the h1
element. Let’s change things up a bit. Say that we want to print the names of several other superheroes. To do this, we modify our render
method to look as follows:
Notice what you see here. We emit a div
that contains the four h1
elements with our superhero names.
Okay, so now we have four h1
elements that each contains the name of a superhero. What if we want to change our h1
element to something like an h3
instead? We can manually update all of these elements as follows:
If you preview what we have, you’ll see something that looks a bit unstyled and plain (see Figure 3.3).
Figure 3.3 Plain vanilla superhero names.
We don’t want to go crazy with the styling here. All we want to do is italicize all these names by using the i
tag, so let’s manually update what we render by making this change:
We went through each h3
element and wrapped the content inside some i
tags. Can you start to see the problem here? What we are doing with our UI is no different than having code that looks as follows:
Every change we want to make to our h1
or h3
elements needs to be duplicated for every instance of them. What if we want to do something even more complex than just modifying the appearance of our elements? What if we want to represent something more complex than the simple examples we’re using so far? What we’re doing right now won’t scale; manually updating every copy of what we want to modify is time-consuming. It is also boring.
Now, here’s a crazy thought: What if everything awesome that we looked at about functions could somehow be applied to how we define our app’s visuals? Wouldn’t that solve all the inefficiencies we’ve highlighted in this section? As it turns out, the answer to that “what if” forms the core of what React is all about. It’s time for you to say hello to the component.
The solution to all of our problems (even the existential ones we grapple with) can be found in React components. React components are reusable chunks of JavaScript that output (via JSX) HTML elements. That sounds really pedestrian for something capable of solving great things, but as you start to build components and gradually turn up the complexity, you’ll see that components are really powerful and every bit as awesome as I’ve portrayed them.
Let’s start by building a couple of components together. To follow along, start with a blank React document:
Nothing exciting is going on in this page. As in the last chapter, this page is pretty barebones, with just a reference to the React and Babel libraries and a div
element that proudly sports an id
value of container
.
Let’s start really simple. We want to use a component to help us print the famous “Hello, world!” text to the screen. As we already know, using just the render
method of ReactDOM
would give us code that looks as follows:
Let’s re-create all of this by using a component. React gives us several ways of creating components, but we are going to create them by using the class syntax. Go ahead and add the following highlighted code just above the existing render
method:
If the class syntax is foreign to you, first check out my online tutorial Using Classes in JavaScript (https://www.kirupa.com/javascript/classy_way_to_create_objects.htm).
Getting back to our code, we have created a new component called HelloWorld
. This is a component because it extends React.Component
. If it didn’t do that, it would just be an empty class that doesn’t do much. Inside our class, you can put all sorts of methods to further define what HelloWorld does. Some methods that you define are special, and React uses them to help your components work their magic. One such mandatory property is render
.
Go ahead and modify our HelloWorld
component by adding the render
method, as shown:
Just like the render
method you saw a few moments earlier as part of ReactDOM.render
, the render
function inside a component is also responsible for dealing with JSX. Let’s modify our render
function to return Hello, componentized world!. Add the following highlighted line:
You’ve told the render
function to return the JSX that represents the Hello, componentized world! text. All that remains is to actually use this component. You use a component after you’ve defined it by calling it. Here we call it from our old friend, the ReactDOM.render
method.
The way you call a component from it is a bit unique. Go ahead and replace the first argument to ReactDOM.render
with the following:
That isn’t a typo! The JSX we use for calling our HelloWorld component is the very HTML-like <HelloWorld/>
. If you preview your page in your browser, you’ll see the text Hello, componentized world! showing up on your screen. If you were holding your breath in suspense, you can relax.
If you have difficulty relaxing after seeing the syntax we used for calling HelloWorld
, stare at the circle in Figure 3.4 a few moments.
Figure 3.4 Just some lighthearted distraction!
Okay, back to reality. What we’ve done so far might seem crazy, but simply think of your <HelloWorld/>
component as a cool and new HTML tag whose functionality you fully have control over. This means you can do all sorts of HTML-y things to it.
For example, go ahead and modify our ReactDOM.render
method to look as follows:
We wrapped our call to the HelloWorld
component inside a div
element, and if you preview this in your browser, everything still works. Let’s go one step further! Instead of having just a single call to HelloWorld
, let’s make a bunch of calls. Modify our ReactDOM.render
method to now look as follows:
Now you’ll see is a bunch of Hello, componentized world! text instances appear. Let’s do one more thing before we move on to something shinier. Go back to our HelloWorld
component declaration and change the text you return to the more traditional Hello, world! value:
Make this one change and then preview your example. This time around, all the various HelloWorld
calls we specified earlier return Hello, world! to the screen. No need to manually modify every HelloWorld
call—that’s a good thing!
Right now, our component does just one thing. It prints Hello, world! to the screen—and only that! That’s the equivalent of having a JavaScript function that looks like this:
Except for one very specific case, that JavaScript function doesn’t seem very useful, does it? To increase the usefulness of this function, we need to modify it to take arguments:
Now this function can be used more generally for a variety of situations, not just one whose output will be 42km.
Something similar applies to your components as well. Just as with functions, you can pass in arguments that alter what your component does. There’s a slight terminology update you need to be on top of. What we call arguments in the function world are known as properties in the component world. Let’s see these properties in action!
You’re now going to modify the HelloWorld
component to allow you to specify who or what you greet besides the generic World. For example, imagine being able to specify Bono as part of the HelloWorld
call and seeing Hello, Bono! appear onscreen.
To add properties to a component, you need to follow two parts of instructions.
Right now, our HelloWorld
component is hard-coded to always send out Hello, world! as part of its return value. We first need to change that behavior by having the return statement print out the value passed in by a property. We need a name to give our property; for this example, we call our property greetTarget
.
To specify the value of greetTarget
as part of our component, we need to make this modification:
You access a property by referencing it via the this.props
property that every component has access to. Notice how you specify this property: You place it inside curly brackets, {
and }
. In JSX, if you want something to get evaluated as an expression, you need to wrap that something inside curly brackets. If you don’t do that, you’ll see the raw text this.props.greetTarget printed out.
After you’ve updated the component definition, all that remains is to pass in the property value as part of the component call. This is done by adding an attribute with the same name as the property, followed by the value you want to pass in. In our example, that involves modifying the HelloWorld
call with the greetTarget
attribute and the value you want to give it.
Go ahead and modify the HelloWorld
calls as follows:
Each HelloWorld
call now has the greetTarget
attribute, along with the name of a superhero (or equivalent mythical being) that we want to greet. If you preview this example in the browser, you’ll see the greetings happily printed out onscreen.
One last point is important to call out before we move on. You are not limited to having just a single property on a component. You can have as many properties as you want, and your props property will easily accommodate any property requests you have without making any fuss.
A few sections ago, I mentioned that components (in JSX) are very similar to regular HTML elements. You saw that when you wrapped a component inside a div
element or specified an attribute and value as part of specifying properties. Just as you can have many HTML elements, your components can have children.
This means you can do something like this:
Here you have a component very cleverly called CleverComponent
, and it has a p
element as a child. From within CleverComponent
, you have the capability to access the p
child element (and any children it has) via the children
property accessed by this.props.children
.
To make sense of all this, let’s look at another really simple example. This time around, we have a component called Buttonify
that wraps its children inside a button. The component looks like this:
You can use this component by calling it via the ReactDOM.render
method, as shown here:
When this code runs, given what the JSX in the Buttonify
component’s render method looked like, you see the words SEND DATA wrapped inside a button element. With the appropriate styling, the result could look comically large, as in Figure 3.5.
Figure 3.5 A large Send Data button.
Getting back to the JSX, notice that we specify a custom property called behavior. This property allows us to specify the button element’s type attribute, and you can see us accessing it via this.props.behavior
in the component definition’s render
method.
There’s more to accessing a component’s children than what you’ve seen here. For example, if your child element is just some text, the this.props.children
property returns a string. If your child element is just a single element (as in our example), the this.props.children
property returns a single component that is not wrapped inside an array. We still need to call out a few more things, but instead of enumerating all the cases and boring you, we’ll bring up those points later as we look at more elaborate examples.
If you want to build an app using React, you can’t wander too far without having to use a component. Trying to build a React app without using a component is kind of like building a JavaScript-based app without using functions. I’m not saying that it can’t be done; it’s just one of those things you don’t do—kind of like the Bad Idea part of the popular Animaniacs Good Idea/Bad Idea sketches (https://www.youtube.com/watch?v=2dJOIf4mdus).
If this witty video doesn’t convince you that you should learn to embrace components, I don’t know what will—except for maybe a future chapter on creating complex components!
Note: If you run into any issues, ask!
If you have any questions or your code isn’t running like you expect, don’t hesitate to ask! Post on the forums at https://forum.kirupa.com and get help from some of the friendliest and most knowledgeable people the Internet has ever brought together!