- Use props to make reusable component templates
- Understand how props are passed to a component
In this lesson, we'll learn how we can turn our React components into dynamic templates using props.
We can define a React component as follows:
A component is a function that takes props as an argument and returns JSX.
As the building blocks of React applications, components are dynamic, in that they can describe a template of JSX in which variable data can be populated. To illustrate dynamic components, we will build an example blogging application. Our application will include the following components:
BlogContent
- contains the content of the blog postComment
- contains one user's commentBlogPost
- the 'top level' React component, which is responsible for rendering bothBlogContent
andComment
Fork and clone this repo if you would like to follow along. Run
npm install && npm start
to get it running. To see the completed app if you do
not code along, the solution
branch is available.
Note: If coding along, the app will initially run with an error. We will fix that shortly into the lesson.
Time to put the dynamic aspect of components to use! Let's start with the
BlogContent
component. Create a BlogComponent.tsx
file in the
src/components
folder with the following:
function BlogContent(props: any) {
return <div>{props.articleText}</div>;
}
export default BlogContent;
You should see something new in the above code. Our function has a parameter
defined called props
. Also, inside the return statement, we have this funky
syntax: {props.articleText}
.
This line is telling React to place the value that props.articleText
represents within the <div>
. Ok, so where does props.articleText
come from?
Note: We gave our props a type of
any
, which we learned is highly discouraged. This is temporary. TypeScript will not compile React components with props that have no explicit type assignment. At the moment, we don't really know what props are, let alone what type to give them. So as we're just starting, we'll give itany
for now and change it later on.
React allows us to pass units of information from a parent component down to a
child component. We call these props. To better understand, let's
demonstrate. Let's see how we can pass information from BlogPost
down to its
child BlogContent
.
Create another file in src/components
called BlogPost.tsx
. Create the
BlogPost
component with the following:
import BlogContent from "./BlogContent";
function BlogPost() {
return (
<div>
<BlogContent articleText="Dear Reader: Bjarne Stroustrup has the perfect lecture oration." />
</div>
);
}
export default BlogPost;
Above we see that when we render the BlogContent
component, we also create a
prop called articleText
that we assign a value of "Dear Reader: Bjarne
Stroustrup has the perfect lecture oration." This value is accessible from
within the BlogContent
component as props.articleText
!
The syntax for creating props for a React component is the same as the syntax
for writing attributes for an HTML tag. For example, to assign a <div>
an
id, we give it an attribute:
<div id="card">Hello!</div>
To assign a prop to a component, we use the same syntax:
function ParentComponent() {
// passing prop to ChildComponent
return <ChildComponent text="Hello!" number={2} />;
}
function ChildComponent(props: any) {
// using the values of the text and number props
return (
<div>
{props.text} {props.number}
</div>
);
}
But remember, this is JSX and not HTML!
One more thing about props: they can be any data type! In our example, we pass a string and a number as props. But we can also pass booleans, objects, functions, etc. as props. Whatever their data type, we must tell TypeScript what to expect.
Before we can learn how to type them, let's expand a bit on props here to understand what they actually look like under the hood. Taking a look at both of our components will give us a better understanding of how data can be passed from one component to another:
// BlogPost.tsx
// PARENT COMPONENT
function BlogPost() {
return (
<div>
{/* BlogContent is being returned from BlogPost */}
{/* Therefore, BlogContent is a child of BlogPost */}
<BlogContent articleText="Dear Reader: Bjarne Stroustrup has the perfect lecture oration." />
</div>
);
}
// BlogContent.tsx
// CHILD COMPONENT
function BlogContent(props: any) {
return <div>{props.articleText}</div>;
}
When one component returns another component, this creates a special relationship between these two components. The component being returned is the child component, and the component returning that child is the parent component.
The only way for a parent component to pass data to its child components is via props.
On this line:
// BlogPost.tsx
<BlogContent articleText="Dear Reader: Bjarne Stroustrup has the perfect lecture oration." />
We are adding a prop of articleText
to our BlogContent
component.
If we add a console.log
in the BlogContent
component to inspect the props:
// BlogContent.tsx
function BlogContent(props: any) {
console.log(props);
return <div>{props.articleText}</div>;
}
We'll see an object with key-value pairs related to the data we passed down from the parent component!
// BlogContent.tsx
console.log(props);
// => { articleText: "Dear Reader: Bjarne Stroustrup has the perfect lecture oration." }
We can add as many additional props as we want by assigning them in the parent component:
// BlogPost.tsx
<BlogContent
articleText="Dear Reader: Bjarne Stroustrup has the perfect lecture oration."
isPublished={true}
minutesToRead={1}
/>
Note: For props that are strings, we don't need to place curly braces around the values; for other data types (numbers, booleans, objects, etc), we need curly braces.
And all of these props will be added as keys on the props object in the child component:
// BlogContent.tsx
console.log(props);
/*
{
articleText: "Dear Reader: Bjarne Stroustrup has the perfect lecture oration.",
isPublished: true,
minutesToRead: 1
}
*/
Having the ability to share this data between components by passing props down
to a child component from a parent makes our components incredibly flexible! For
example, here's how we could expand our BlogContent
component based on those
additional props:
function BlogContent(props: any) {
console.log(props);
if (!props.isPublished) {
// hide unpublished content
// return null means "don't display any DOM elements here"
return null;
} else {
// show published content
return (
<div>
<h1>{props.articleText}</h1>
<p>{props.minutesToRead} minutes to read</p>
</div>
);
}
}
Above, we are using conditional rendering to only
display the blog content if it is published, based on the isPublished
prop.
We've learned that props become an object. This makes typing them quite easy! We
already learned how to type objects with interfaces, and we can do
the same with props. Let's try it with our BlogContent
props.
Our BlogContent
props and their expected types are:
articleText
:string
isPublished
:boolean
minutesToRead
:number
We can create an interface detailing that. Let's name it Props
. Because these
props specifically belong to our BlogContent
, we'll define it at the top of
its file:
// BlogContent.tsx
interface Props {
articleText: string;
isPublished: boolean;
minutesToRead: number;
}
Instead of type any
, we can now assign the props the type of our Props
interface:
function BlogContent(props: Props) {
// ...
}
We still need a Comment
component that we can use for each comment in a
BlogPost
. Create a Comment.tsx
file in the src/components
directory.
Let's have the component return a div with the comment text. Comments will be
variable since not everyone will leave the same one. Instead of hard coding a
comment, we can make it variable with props. Let's name that prop commentText
,
which we will expect to be of type string
.
We first have to create a separate interface for our Comment
props, because it
will not look the same as the ones on BlogContent
. We can still name it
Props
, as we're working in a separate file.
// Comment.tsx
interface Props {
commentText: string;
}
function Comment(props: Props) {
return <div>{props.commentText}</div>;
}
export default Comment;
This component, when used, will display content that is passed down to it,
allowing us to pass different content to multiple Comment
components. Let's
add them in. Of course, with components being re-usable, we can make as many as
we want:
// BlogPost.tsx
import Comment from "./Comment";
function BlogPost() {
return (
<div>
<BlogContent articleText="Dear Reader: Bjarne Stroustrup has the perfect lecture oration." />
<Comment />
<Comment />
<Comment />
</div>
);
}
...and just as before, we can pass content data down to them:
// BlogPost.tsx
function BlogPost() {
return (
<div>
<BlogContent
articleText="Dear Reader: Bjarne Stroustrup has the perfect lecture oration."
isPublished={true}
minutesToRead={1}
/>
<Comment commentText="I agree with this statement. - Angela Merkel" />
<Comment commentText="A universal truth. - Noam Chomsky" />
<Comment commentText="Truth is singular. Its ‘versions’ are mistruths. - Sonmi-451" />
</div>
);
}
There is quite a bit going on here. Most notably, we are passing information
from a parent component to many child components. Specifically, we are doing
this by creating a prop called commentText
to pass to each Comment
component, which is then accessible in each instance of Comment
as
props.commentText
. Let's expand the HTML this would ultimately render:
<div>
<div>Dear Reader: Bjarne Stroustrup has the perfect lecture oration.</div>
<div>I agree with this statement. - Angela Merkel</div>
<div>A universal truth. - Noam Chomsky</div>
<div>Truth is singular. Its ‘versions’ are mistruths - Sonmi-451</div>
</div>
...but seeing is believing so let's look at this in technicolor! Following is an inspection of the real live DOM elements that React rendered when we blasted this code into a new application (classes, IDs, and minor CSS have been added for a better visual display, feel free to add them to yours):
Alright now! Take a moment. Stretch your limbs, make a sandwich, let the glorious paradigm sink in. Dynamic components are a core facet of React programming, and most of what we do as React programmers builds upon them.
While HTML elements are the basic building blocks of a website, (for example, a
<div>
), a React application usually consists of several React components
combined together. Unlike simple HTML elements, React components are smarter and
bigger. They allow you to do much more and incorporate logic into how content
displays.
Components:
- are modular, reusable, and enable a 'templating' functionality
- help us organize our user interface's logic and presentation
- enable us to think about each piece in isolation, and apply structure to complex programs
Props:
- are passed from a parent component to a child component
- can be accessed in the child components via an object that is passed into our component function as an argument
- can hold any kind of data (strings, numbers, booleans, objects, even functions!)
Going forward we will expand on what we can do with components, how they fit into the larger React landscape, and what built-in functionality they come with.