ReactJS project based training with Road Trip Planner App

Building Planning Page components : Part 1

Let’s add some real stuff to our Planning component now. We’ll start with the left-hand side “planning-board” which further is made up of an <h1> element and a component <PlanningCard/>

This time we are building ground-up, first the lowermost components and then combining them as we go up.

<TripBoard/>

This component represents all of the existing locations entered by the user, using another component, it receives the destination data and passes it on to the <DestinationCard/> to represent each location separately, so yeah we’ve got another child component to build first (can’t help it getting deep :) , let’s look at the component tree

<DestinationCard/>

<TripBoard/> passes on the received data of destinations to this component one at a time, It renders the following elements for the name of the location, date and time of departure, and an icon button to delete this Destination card from the board.

Inside the DestinationCard.jsx under the component folder, write the following code,

import { AiFillDelete } from "react-icons/ai";

const DestinationCard = ({ name, id, date, time, remove_location }) => {
  return (
    <div className="dest-card">
      <div className="dest-name">{name}</div>
      <p>{date}</p>
      <p>{time}</p>
      <button className="dlt-btn" onClick={() => remove_location(id)}>
        <AiFillDelete size="2rem" />
      </button>
    </div>
  );
};

export default DestinationCard;

đŸ‘† DestinationCard.jsx

It’s a stateless component, and you just need to import an icon for the delete feature from react icons. And note that the prop object has been destructured here, so rather than using the prop-dot-operator, we are directly using the props by name.

Notice that among data props we also have a functional prop remove_location(), which is passed as an event handler, meaning whenever the delete icon is clicked remove_location function is called with an id. We’ll find later how the remove_location function works, for now, it’s okay to just know that it will delete the destination card it is on.

Now, to see how this component will look on the page, let’s add it directly to the left side of Planning.jsx that is planning-board.

import React from "react";
import DestinationCard from "../components/DestinationCard";

class Planning extends React.Component {
  render() {
    return (
      <div className="planning-container">
        <div className="planning-board">
          <h1>Create a new Trip</h1>
          <DestinationCard
            name={"Udaipur, Rajasthan"}
            id={"7896y78"}
            date={"25-12-22"}
            time={"22:00"}
            remove_location={() => {}}
          />
        </div>
      </div>
    );
  }
}

export default Planning;

đŸ‘† Planning.jsx

For now, pass some demo data of your choice to the props of DestinationCard, for the function prop keep it as an empty function so that it doesn't throw an error.

So far it looks something like this on the page,

We need to add styling for DestinationCard to make it look like the final version on the page

đŸ‘†Final DestinationCard on the page

According to the class names you have set to your elements, write the following CSS inside your Planning.css

.dest-card {
  display: flex;
  font-size: 1.5rem;
  justify-content: space-between;
  align-items: center;
  width: 100%;
  background-color: white;
  color: var(--night-black);
  border-radius: 1rem;
  height: 160px;
  padding: 0.5rem 1rem;
}

.dest-card:hover {
  border: solid 1px;
}

/*As it is a flex item we can customise space occupied by a certain item inside the flexbox*/
.dest-name {
  width: 30%; 
}

.dlt-btn:hover {
  background-color: var(--cloud-gray);
}

Perfect! ✨

đŸ‘† DestintationCard on Page after adding Styling

Let’s now go back to the <TripBoard/>, It should look like this on the page, expanding and shrinking according to the number of <DestinationCard/> it holds,

Start with importing DestinationCard.jsx component to the TripBoard.jsx, but try to make sense of the following code, on your own first.

import svgs from "../assets";
import DestinationCard from "./DestinationCard";

const TripBoard = (props) => {
  const demo_props = {
    locations: [
      { id: 742, name: "Jaipur, Rajasthan", date: "2022-12-20", time: "07:30" },
      { id: 489, name: "Jodhpur, Rajasthan", date: "2022-12-23", time: "10:00" },
      { id: 201, name: "Alwar, Rajasthan", date: "2022-12-28", time: "08:55" },
    ],
  };
  function remove_location() {}
  return (
    <div className="trip-board">
      {demo_props.locations.map(({ id, name, date, time }) => (
        <DestinationCard
          key={id}
          remove_location={remove_location}
          id={id}
          name={name}
          date={date}
          time={time}
        />
      ))}
    </div>
  );
};

export default TripBoard;

đŸ‘†TripBoard.jsx

props: TripBoard component accepts an array of destination data inside props. As we have no real data entered by the user yet, I have created a demo_props object, altho the TripBoard component accepts a prop object from its parent component, as we have no information about the parent component and no props available as of now, we’ll make use of demo_props an object replicating the actual props object. Also for now I have declared remove_location as an empty function inside the same component but later it’ll be received as a prop.

If you aren’t familiar with map(), filter() or any of the array functions in javascript, please go and look it up, also read on destructuring in javascript.

Now let’s see what is happening here, inside a div called “trip-board”, we have written a javascript statement using { } , the map function is applied on the locations array which creates a new array of <DestinationCard/> components, one for each item in locations array. Ultimately this array of <DestinationCard/> is rendered inside the “trip-board”.

Remember that while rendering a list of items like we just did, it’s almost always essential to include unique key values for each item in the list, to avoid unnecessary errors and bugs, especially when you know that the order of items might change in the future.

Now let’s see how it looks on the page,

Replace the single <DestinationCard/> with <TripBoard/> inside Planning.jsx.

import React from "react";
import "./planning.css";
import TripBoard from './../components/TripBoard';

class Planning extends React.Component {
  render() {
    return (
      <div className="planning-container">
        <div className="planning-board">
          <h1>Create a new Trip</h1>
          <TripBoard/>
        </div>
      </div>
    );
  }
}
export default Planning;

Planning.jsx

the “planning-board” div, left side of our planning page.

the “planning-board” div, left side of our planning page.

Let’s quickly add the styling now, for “trip-board” and for “planning-board”, add the following CSS to your planning.css

.trip-board {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  border: white solid 1px;
  border-radius: 1rem;
  padding: 1rem;
  width: 100%;
  min-height: 200px;
  max-height: max-content;
}

.planning-board {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  padding: 1rem;
  width: 65%;
  min-height: 40vh;
  max-height: max-content;
  color: white;
  background-color: var(--night-black);
  border-radius: 0 2rem 2rem 0;
  box-shadow: 2px 2px 15px 4px grey;
}

Final planning-board

Final planning-board

Moving on to the right side of the Planning page and the most essential part of this app,