Mini Calendar: React Components, Props and Project Structure
Rana Emad
Posted on June 3, 2020
Still React-ing! Now that we are more confident in our JSX skills and we know how to set up a development environment to work with, it's time to take a step further and get to know what React is really made of.
Components
Apparently, the component is the main building unit in React so we need to get a good grasp on how it works, as our lives would be pretty much revolving around it.
I found 2 types of Components:
I have tried to find out which is better or which is more used and why, but couldn't find solid information.
Some say class components give access to additional features like state, but others reply that the same functionality can be compensated for by hooks in function components. I found lots of arguments, but nothing real, so, as agreed these are more advanced topics for future Rana to decide upon. For present Rana the only difference between them both is the syntax, so let's check it out!
Function Component
Just your normal Javascript function, but returns a JSX element, the only unusual thing about it, is that the first letter of its name will be capitalized in order for us to be able to distinguish it from other markup tags, when it's called.
function MyFunctionComponent(){
return (
<div>
<h1>Hello, Function Component!</h1>
</div>
);
}
OR if you're an ES6 or later person
const MyFunctionalComponent=()=>(
<div>
<h1>Hello, Function Component!</h1>
</div>
);
Class Component
Your usual class, but it has to extend the React.Component class to get all the React perks. Also, the JSX code to be rendered has to be returned by, obviously, the render method.
class MyClassComponent extends React.Component {
render() {
return (
<div>
<h1>Hello, Class Component!</h1>
</div>
);
}
}
Nested Components
A horrible mistake I did earlier is, I insisted on rendering multiple sibling elements at once and that's not possible, so remember kids, always wrap all your elements in one and only one element
OR
I found another magic trick where we can enclose all our elements in a React.Fragment element
<React.Fragment>
<div>1</div>
<div>2</div>
<div>3</div>
</ React.Fragment>
But what if I want to render a component within a component? Nest away!
If I were a component, either a function or a class and was called, I would look like this:
<Rana />
I have a self closing tag, because I am writing on an empty stomach, but there is a sexy pasta alfredo component sitting right beside me looking like this:
<PastaAlfredo />
Of course, who am I to resist? The pasta component and I need to become one so it stopped sitting beside me and it is now sitting in my stomach, so now I look like this:
<Rana>
<PastaAlfredo />
</Rana>
Props
If you want to nest components you're probably going to want to pass those poor children some data to modify their behavior. That's where props come in handy.
For function components we just pass a props variable (it could be any name of our choice, but that's how the convention goes) as an argument and for class components we can access the props using this.props
So far so good we have nice concepts in hand. Now let's try and use them to build something!
Build What?
Last time, thanks to our marvelous React coding skills, we were capable of knowing what day today is, now we will take what we already have a tad further and mix it to build a mini calendar for the whole month.
The code can be found HERE
Project Structure
Let's start by running npx create-react-app .
in our root folder of choice!
Previously, we postponed going through the create-react-app project structure for the future and the future is now!
The public folder
Or what I like to call things that doesn't really concern me folder.
- It is where our one and only HTML file is located. We don't need more than that to render our very useful code.
- The app's manifest file, favicon and logos for the app's service worker to work properly if enabled.
So, we will rarely need to change anything in that folder.
The src folder
That's probably where we will be living while developing.
I was checking how I was going to structure the folders for adding components to keep myself organized and there were multiple options from which I liked a couple. Both of them start by creating a components folder within the src folder and then:
- Group files by type. Separate the CSS from the logic where each would be in a separate folder and each file would be named after its component
- Group files by component. Create a folder with the name of the component within the components folder and add everything related to it in it whether it's CSS or Javascript.
I chose to go with the later for now to make it easier for me to move components from project to project until I dig deeper in component reusability.
The Plan
Let's try and define the components we need!
First of all, we will need a convenient space to contain all our days, so let's call our first component Month.
Second, we will need a Day component to represent each day in our month, so there goes our second component!
We will include the unique day brands in the Day component and we will include all our Day components in our Month component. That's a solid plan.
Day
I will be using function components for now and save class components for later.
Let me start by creating a components folder and inside it create a Day folder that includes an empty Day.js file.
Baby steps, so for now let's just add the date inside our Day component. I'll just put a dummy 1 until we figure out our next step.
import React from "react";
function Day() {
return <div className="day">1</div>;
}
export default Day;
Month
We will create a folder structure for the Month component similar to our Day component.
In our Month.js file we will import our Day component and add elements for the month heading and the days.
import React from "react";
import Day from "../Day/Day";
function Month() {
return (
<div className="month">
<div className="heading">June</div>
<div className="days">
<Day />
</div>
</div>
);
}
export default Month;
App
In our App.js file let's clean up what we don't need and add our Month component to view our progress on screen as we go.
import React from "react";
import "./App.css";
import Month from "./components/Month/Month";
function App() {
return (
<div className="app">
<Month />
</div>
);
}
export default App;
I cleared the App.css file and just set the styles that will affect the elements globally.
@import url("https://fonts.googleapis.com/css2?family=Great+Vibes&display=swap");
* {
box-sizing: border-box;
}
html,
body,
#root,
.app {
height: 100%;
}
Month
Great! Everything is rendered. Now we only have 1 day and we need to add all the days. I am definitely not going to add about 30 days manually, so it's loop O'clock.. But how?
React loves arrays and so do we! We are going to loop normally from 1 to the last day of the current month and for each loop we're going to push a Day component in our days array.
To render the days array we will just wrap it with curly braces and they'll take care of everything.
function Month() {
let days = [];
const currentDate = new Date();
const daysOfMonth = new Date(
currentDate.getFullYear(),
currentDate.getMonth() + 1,
0
).getDate();
for (let i = 1; i <= daysOfMonth; i++) {
days.push(<Day />);
}
return (
<div className="month">
<div className="heading">June</div>
<div className="days">
{days}
</div>
</div>
);
}
Before we sail any further, let me just throw some grid view styling in a Month.css file and import it into our component to be able to get a better understanding of what goes where.
.month {
height: 100%;
display: grid;
grid-template-rows: 5% auto;
padding: 1rem;
}
.month .heading {
text-align: center;
align-self: center;
font-family: "Great Vibes", cursive;
font-size: 42px;
color: #ab1111;
font-weight: 600;
}
.month .days {
display: grid;
grid-template-columns: repeat(6, 1fr);
grid-gap: 2px;
}
@media screen and (max-width: 950px) {
.month .days {
grid-template-columns: repeat(5, 1fr);
}
}
@media screen and (max-width: 800px) {
.month .days {
grid-template-columns: repeat(3, 1fr);
}
}
@media screen and (max-width: 400px) {
.month .days {
grid-template-columns: 1fr;
}
}
Lovely! Now we have 30 ones rendered to the screen and now we need to replace them with the actual dates. Props to the rescue!
In our loop let's send the date as a prop in our loop
for (let i = 1; i <= daysOfMonth; i++) {
days.push(<Day date={i} />);
}
In our Day component let's replace our dummy text with the actual date.
function Day(props) {
return <div className="day">{props.date}</div>;
}
Everything is working well, but I got prompted a warning in the console: Warning: Each child in a list should have a unique "key" prop.
To get rid of that I added a key prop for the Day component and set it with an incremental unique key for each.
Great! Now that everything is set, we need to add our unique day brands we implemented previously in
To be able to do that we need to send what day of the week it is to our Day component as a prop and while we're at it let's get the month in the heading dynamically, as well.
let months = [];
months[0] = "January";
months[1] = "February";
months[2] = "March";
months[3] = "April";
months[4] = "May";
months[5] = "June";
months[6] = "July";
months[7] = "August";
months[8] = "September";
months[9] = "October";
months[10] = "November";
months[11] = "December";
function Month(){
let days = [];
const currentDate = new Date();
const daysOfMonth = new Date(
currentDate.getFullYear(),
currentDate.getMonth() + 1,
0
).getDate();
for (let i = 1; i <= daysOfMonth; i++) {
let weekDay = new Date(
currentDate.getFullYear(),
currentDate.getMonth(),
i
).getDay();
days.push(<Day key={"day" + i} date={i} weekDay={weekDay} />);
}
return (
<div className="month">
<div className="heading">{months[currentDate.getMonth()]}</div>
<div className="days">{days}</div>
</div>
);
}
Day
For our Day component we will add an array of days and use it to assign classes for custom styling.
let week = [];
week[0] = "Sunday";
week[1] = "Monday";
week[2] = "Tuesday";
week[3] = "Wednesday";
week[4] = "Thursday";
week[5] = "Friday";
week[6] = "Saturday";
function Day(props) {
return (
<div className={"day " + week[props.weekDay].toLowerCase()}>
<div className="date">{props.date}</div>
<div className="weekday">{week[props.weekDay]}</div>
</div>
);
}
Now we have all the data we need so let's add our SVG images to our Day folder and fill a Day.css with magic and import it!
.day {
padding: 1.5rem;
background-repeat: no-repeat;
background-size: 50%;
background-position: bottom;
opacity: 0.7;
box-shadow: 1px 1px 6px 0px black;
display: flex;
flex-direction: row;
justify-content: flex-start;
}
.day.sunday {
background-size: 40%;
}
.sunday {
background-image: url("./sunday.svg");
}
.monday {
background-image: url("./monday.svg");
}
.tuesday {
background-image: url("./tuesday.svg");
}
.wednesday {
background-image: url("./wednesday.svg");
}
.thursday {
background-image: url("./thursday.svg");
}
.friday {
background-image: url("./friday.svg");
}
.saturday {
background-image: url("./saturday.svg");
}
.weekday {
margin: 0 auto;
font-family: "Great Vibes", cursive;
font-size: 1.5rem;
}
.wednesday .weekday {
color: #f9a727;
text-shadow: 1px 1px #3f3d55;
}
.saturday .weekday {
color: #e50914;
text-shadow: 1px 1px #00bfa6;
}
.thursday .weekday {
color: #2f2f41;
text-shadow: 1px 1px #00b0ff;
}
.friday .weekday {
color: #d83e75;
text-shadow: 1px 1px #2f2f41;
}
.sunday .weekday {
color: #ff6584;
text-shadow: 1px 1px #3f3d55;
}
.monday .weekday {
color: #0e01bf;
text-shadow: 1px 1px #ffb8b8;
}
.tuesday .weekday {
color: #36799a;
text-shadow: 1px 1px #ffb8b8;
}
Hooray! Our calendar is all set, but I am getting a little greedy. I want to highlight today so that I won't be lost.
In our Month.js file let's send a prop to set that.
for (let i = 1; i <= daysOfMonth; i++) {
let weekDay = new Date(
currentDate.getFullYear(),
currentDate.getMonth(),
i
).getDay();
let today = "";
if (i === currentDate.getDate()) {
today = " today ";
}
days.push(<Day key={"day" + i} date={i} weekDay={weekDay} today={today} />);
}
In our Day.js file let's read that prop and set it as a class.
function Day(props) {
return (
<div className={"day " + props.today + week[props.weekDay].toLowerCase()}>
<div className="date">{props.date}</div>
<div className="weekday">{week[props.weekDay]}</div>
</div>
);
}
Finally, let's add styling in Day.css to highlight today.
.today {
box-shadow: 0px 0px 5px 2px #770909;
opacity: 1;
}
Voila! We have our calendar!
By this mini calendar I shall end my second baby step towards React greatness, until we meet in another one.
Any feedback or advice is always welcome. Reach out to me here, on Twitter, there and everywhere!
Posted on June 3, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.
Related
October 3, 2024
August 26, 2024