React: Component Nesting, Re-usability & Configuration
Intro
One of the beautiful things about React is that the page is built up using multiple reusable components. These components can be nested inside of each other, reused several times, and can also be individually configured. For example a button. All of the buttons will mostly look the same, however they will link to different places and probably have different text on them.
Let’s start by taking a look at a good use case for components. Let’s say we are rendering a comments section. We are going to be rendering the same thing over and over again. In this example we are using Semantic UI for styling and the Faker module to generate random data.
import React from "react";
import ReactDOM from "react-dom";
import Faker from "faker";
const App = () => {
return (
<div className="ui container comments">
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src={Faker.image.avatar()} />
</a>
<div className="content">
<a href="/" className="author">
{Faker.name.firstName()}
</a>
<div className="metadata">
<span className="date">Yesterday @ 10AM</span>
</div>
<div className="text">{Faker.lorem.sentences()}</div>
</div>
</div>
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src={Faker.image.avatar()} />
</a>
<div className="content">
<a href="/" className="author">
{Faker.name.firstName()}
</a>
<div className="metadata">
<span className="date">Yesterday @ 10AM</span>
</div>
<div className="text">{Faker.lorem.sentences()}</div>
</div>
</div>
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src={Faker.image.avatar()} />
</a>
<div className="content">
<a href="/" className="author">
{Faker.name.firstName()}
</a>
<div className="metadata">
<span className="date">Yesterday @ 10AM</span>
</div>
<div className="text">{Faker.lorem.sentences()}</div>
</div>
</div>
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#root"));
And all that gives us this:
Creating Reusable Components
- Identify JSX that appears to be duplicated
- What is the purpose of that block of JSX? Think of a descriptive name for what it does.
- Create a new file to house this new component – it should have the same name as the component.
- Create a new component in the new file, paste the JSX into it.
- Make the new component configurable by using React’s ‘props’ system
Create New File
We identify the repetitive block which would be each individual comment, then in src we create a new file that describes this element.
and we paste in our repetitive block
import React from "react";
const CommentDetail = () => {
return (
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src={Faker.image.avatar()} />
</a>
<div className="content">
<a href="/" className="author">
{Faker.name.firstName()}
</a>
<div className="metadata">
<span className="date">Yesterday @ 10AM</span>
</div>
<div className="text">{Faker.lorem.sentences()}</div>
</div>
</div>
);
};
export default CommentDetail;
Note that we export the component at the end to make it available in the rest of the app. Then we have to make this component configurable using Props.
Connect Component to Index
Because we have exported the component to it’s own file/module, we can now import it into our index.
import React from "react";
import ReactDOM from "react-dom";
import Faker from "faker";
import CommentDetail from './CommentDetail';
const App = () => {
return (
<div className="ui container comments">
<CommentDetail />
</div>
);
};
ReactDOM.render(<App />, document.querySelector("#root"));
And note that we have replaced the long string of JSX with just <CommentDetail>
. Components are the one exception in JSX where we don’t use the {}
syntax to call a function.
And of course we could just repeat that multiple times to get several comments.
const App = () => {
return (
<div className="ui container comments">
<CommentDetail />
<CommentDetail />
<CommentDetail />
<CommentDetail />
<CommentDetail />
</div>
);
};
Configuring Components With Props
Props are a system for passing data from a parent component to a child component. The goal is to customize or configure a child component. A child cannot pass data to a parent directly using the props system. Props is short for properties.
The syntax to provide props is very similar to how you would add any property to an HTML tag.
const App = () => {
return (
<div className="ui container comments">
<CommentDetail author="Nick" />
<CommentDetail author="David" />
<CommentDetail author="Margaret" />
<CommentDetail author="Sally" />
<CommentDetail author="Balthazar" />
</div>
);
};
In this case author
is the name of the prop, and "Nick"
or the other names would be the value of the prop. Then if we move over to the CommentDetail component the prop is provided as the first argument to the function.
const CommentDetail = (props) => {
console.log(props);
return (
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src={Faker.image.avatar()} />
</a>
<div className="content">
<a href="/" className="author">
{Faker.name.firstName()}
</a>
<div className="metadata">
<span className="date">Yesterday @ 10AM</span>
</div>
<div className="text">{Faker.lorem.sentences()}</div>
</div>
</div>
);
};
And we can see that we have console.log’d the props there which gives us this in the console.
Then we replace our Faker name with the prop name props.author
const CommentDetail = (props) => {
console.log(props);
return (
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src={Faker.image.avatar()} />
</a>
<div className="content">
<a href="/" className="author">
{props.author}
</a>
<div className="metadata">
<span className="date">Yesterday @ 10AM</span>
</div>
<div className="text">{Faker.lorem.sentences()}</div>
</div>
</div>
);
};
And our Faker names have now been replaced by our Prop names in our component.
Multiple Props
To add additional props you just add additional properties to the component and then reference them in the component. Like so.
<CommentDetail author="Nick" timeAgo="3 Days Ago" />
<span>{props.timeAgo}</span>
So altogether that would be this.
// app.js
const App = () => {
return (
<div className="ui container comments">
<CommentDetail author="Nick" timeAgo="3 Days Ago" />
<CommentDetail author="David" timeAgo="4 Days Ago" />
<CommentDetail author="Margaret" timeAgo="10 Days Ago" />
<CommentDetail author="Sally" timeAgo="15 Days Ago" />
<CommentDetail author="Balthazar" timeAgo="40 Days Ago" />
</div>
);
};
// commentdetail.js
const CommentDetail = (props) => {
console.log(props);
return (
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src={Faker.image.avatar()} />
</a>
<div className="content">
<a href="/" className="author">
{props.author}
</a>
<div className="metadata">
<span className="date">{props.timeAgo}</span>
</div>
<div className="text">{Faker.lorem.sentences()}</div>
</div>
</div>
);
};
We can mix in variables as well as strings into our props properties.
const App = () => {
return (
<div className="ui container comments">
<CommentDetail
author="Nick"
timeAgo="3 Days Ago"
content={Faker.lorem.sentences()}
profilePic={Faker.image.avatar()}
/>
<CommentDetail
author="David"
timeAgo="4 Days Ago"
content={Faker.lorem.sentences()}
profilePic={Faker.image.avatar()}
/>
<CommentDetail
author="Margaret"
timeAgo="10 Days Ago"
content={Faker.lorem.sentences()}
profilePic={Faker.image.avatar()}
/>
<CommentDetail
author="Sally"
timeAgo="15 Days Ago"
content={Faker.lorem.sentences()}
profilePic={Faker.image.avatar()}
/>
<CommentDetail
author="Balthazar"
timeAgo="40 Days Ago"
content={Faker.lorem.sentences()}
profilePic={Faker.image.avatar()}
/>
</div>
);
};
const CommentDetail = props => {
console.log(props);
return (
<div className="comment">
<a href="/" className="avatar">
<img alt="avatar" src={props.profilePic} />
</a>
<div className="content">
<a href="/" className="author">
{props.author}
</a>
<div className="metadata">
<span className="date">{props.timeAgo}</span>
</div>
<div className="text">{props.content}</div>
</div>
</div>
);
};
Nesting Components
To nest components inside of other components we turn the inside component into a prop of the parent component. Let us take a wrapper for each of these comments that is an approval card with two buttons.
import React from "react";
const ApprovalCard = (props) => {
return (
<div className="ui card">
<div className="content"> COMMENT GOES HERE </div>
<div className="extra content">
<div className="ui two buttons">
<div className="ui basic green button">Approve</div>
<div className="ui basic red button">Reject</div>
</div>
</div>
</div>
);
};
export default ApprovalCard;
Then we go back to our index and we nest one of our comments inside of this Approval Card.
<ApprovalCard>
<CommentDetail
author="Nick"
timeAgo="3 Days Ago"
content={Faker.lorem.sentences()}
profilePic={Faker.image.avatar()}
/>
</ApprovalCard>
However this will still not display the comment yet. If we console log the props of the Approval Card we can see why.
The Approval Card now has a property called children, so to reference this we would call props.children
.
import React from "react";
const ApprovalCard = (props) => {
console.log(props);
return (
<div className="ui card">
<div className="content"> {props.children} </div>
<div className="extra content">
<div className="ui two buttons">
<div className="ui basic green button">Approve</div>
<div className="ui basic red button">Reject</div>
</div>
</div>
</div>
);
};
export default ApprovalCard;
With this method we can change the content inside of the Approval Card using the index instead of the CommentDetail.js component. That component will remain static and display whatever children we decide to put inside of it now.
Comments
Recent Work
Basalt
basalt.softwareFree desktop AI Chat client, designed for developers and businesses. Unlocks advanced model settings only available in the API. Includes quality of life features like custom syntax highlighting.
BidBear
bidbear.ioBidbear is a report automation tool. It downloads Amazon Seller and Advertising reports, daily, to a private database. It then merges and formats the data into beautiful, on demand, exportable performance reports.