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.
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.
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 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:
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
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).
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
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.
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
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
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
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
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
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.
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!
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
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,
}. 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:
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
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.
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
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 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!