Components and Props

Components and Props

JS Skills Needed

Mastering key JavaScript concepts such as objects, JSON, destructuring, and template literals equips you with the essential tools to effectively utilize React's components and props. While these JavaScript concepts are fundamental for understanding React's components and props, they can be complex and challenging to grasp, requiring focused learning and consistent practice. Take your time with these topics, and don't be afraid to revisit them as you progress through the course.

Objects

Objects in JavaScript are standalone entities that can have properties and behavior. They are essentially collections of key-value pairs, where keys are property names and values are the corresponding property values. Objects are one of the most important data structures in JavaScript, and they are used extensively in React.

In this first example, we are creating a simple JavaScript object called user with properties name and age, and then demonstrating how to access and modify those properties.

// Create the object and assign it to a variable
let user = { name: "John", age: 25 };
 
// Access the properties of the object
console.log(user.name); // Outputs: 'John'
console.log(user.age); // Outputs: 25
 
// Modify the properties of the object
user.name = "Ling";
user.age = 30;
 
// Now, if we access the properties again, we'll see the updated values
console.log(user.name); // Outputs: 'Ling'
console.log(user.age); // Outputs: 30

The second example expands on the first by introducing a more complex, nested object, which includes an address object and a coordinates object within address; this example further demonstrates how to access and modify properties in nested objects.

// Create the object and assign it to a variable
let user = {
  name: "John",
  age: 25,
  address: {
    street: "123 Main St",
    city: "Calgary",
    coordinates: {
      lat: 50.067,
      long: 14.467,
    },
  },
};
 
// Access the properties of the object
console.log(user.name); // Outputs: 'John'
console.log(user.address.city); // Outputs: 'Calgary'
console.log(user.address.coordinates.lat); // Outputs: 50.067
 
// Modify the properties of the object
user.name = "Ling";
user.address.city = "Edmonton";
user.address.coordinates.lat = 60.087;
 
// Now, if we access the properties again, we'll see the updated values
console.log(user.name); // Outputs: 'Ling'
console.log(user.address.city); // Outputs: 'Edmonton'
console.log(user.address.coordinates.lat); // Outputs: 60.087
️💡

JavaScript is unique amongst many object-oriented (OO) languages in that it doesn't inherently utilize classes for object-oriented programming. Instead, JavaScript employs a prototype-based inheritance model, which is a form of object-oriented programming where classes are not present, and behavior reuse (known as inheritance) is performed via a process of cloning existing objects that serve as prototypes.

This approach stands in contrast to many other OO languages like Java, C++, or Python, where classes are defined and then objects are created from these classes. In JavaScript, you create objects directly and then link them to a prototype object for behavior reuse.

However, it's worth noting that as of ES6 (ECMAScript 2015), JavaScript introduced a class syntax that provides a much cleaner and simpler way to achieve object-oriented behavior similar to other OO languages. Yet, under the hood, JavaScript still uses its prototype-based model — the class syntax is syntactic sugar over JavaScript's existing prototype-based inheritance.

JSON

JSON, which stands for JavaScript Object Notation, is a lightweight data-interchange format that is easy for humans to read and write and easy for machines to parse and generate. JSON is purely a data format — it contains only properties, no methods. It has become a popular data interchange format due to its simplicity and lightweight nature, making it ideal for data storage and transmission over the network in web APIs.

JSON data format is quite similar to a JavaScript object. For instance, a JSON representation of a person could look like this:

{
  "name": "John",
  "age": 25,
  "city": "Calgary"
}

In this example, we have a JSON object that represents a person with properties name, age, and city. The keys in a JSON object are always strings, while the values can be a string, number, boolean, another JSON object, an array, or null.

Destructuring

Destructuring assignment in JavaScript is a syntax feature that enables a developer to unpack values from objects into distinct variables. This can greatly simplify code that needs to access multiple properties of an object. Instead of accessing properties one by one using dot notation, you can extract multiple properties in one statement.

Consider an object person defined as follows:

const person = {
  name: "John",
  age: 25,
  city: "Calgary",
};

Instead of accessing the properties individually like person.name, person.age, and person.city, with destructuring, you can extract all these properties in a single line

const { name, age, city } = person;

Now, name, age, and city are variables in their own right, each holding the corresponding value from the person object. This can make your code cleaner and more readable, especially when working with objects that have many properties.

Template Literals

Template literals in JavaScript are a way to output variables in the string. Introduced in ES6, they provide a more readable and concise way to work with strings. Template literals are enclosed by the backtick (`) character instead of double or single quotes. They can contain placeholders, indicated by the dollar sign and curly braces ( ${expression}). The expressions in the placeholders and the text between them get passed to a function.

Template literals enhance readability by allowing direct embedding of expressions within strings, eliminating the need for the + operator used in messy string concatenation like const greeting = "Hello" + name. They support:

  • multiline strings without escape characters, e.g. \n
  • simplifying handling of large text blocks or HTML generation
  • seamless integration of any valid JavaScript expression, including functions and arithmetic, directly into the string

Example 1:

let name = "Bob";
let greeting = `Hello, my name is ${name}.`;
console.log(greeting); // Output: "Hello, my name is Bob."

In this example, the name variable is inserted directly into the string using the $ syntax.

Example 2:

let item = "apple";
let quantity = 5;
let price = 0.5;
 
let receipt = `You bought ${quantity} ${item}${
  quantity > 1 ? "s" : ""
}. Total cost is $${(quantity * price).toFixed(2)}.`;
console.log(receipt); // Output: "You bought 5 apples. Total cost is $2.50."

In this example, we're not only inserting variables into the string, but we're also using JavaScript expressions. The ${quantity > 1 ? 's' : ''} part is a ternary operator that adds an 's' if more than one item is bought. The total cost is calculated with ${(quantity \* price).toFixed(2)}, showing how you can call functions like toFixed within a template literal.

Tailwind CSS Skills Needed

Introduction

Tailwind CSS is a utility-first CSS framework that provides low-level utility classes to build custom designs. Unlike traditional CSS frameworks that offer pre-designed components, Tailwind allows developers to compose complex designs directly in their markup without leaving HTML.

Why use Tailwind CSS?

  • Customization: Tailwind is highly customizable, enabling unique design creation instead of enforcing predetermined styles.
  • Responsiveness: It has built-in responsive design utilities, making it easier to design for various screen sizes.
  • Efficiency: By composing classes in your markup, you can create complex designs without switching between HTML and CSS files, improving development speed.

Example:

<button className="px-4 py-2 text-white bg-blue-500 rounded hover:bg-blue-600">
  Click me
</button>

In the example above, we use Tailwind's utility classes like px-4 (padding on the x-axis), py-2 (padding on the y-axis), text-white (text color), bg-blue-500 (background color), and rounded (border radius) to style a button in a React component. The hover:bg-blue-600 class applies a darker background color on hover.

💡

You are not expected to be an expert in Tailwind CSS for this course. In addition, this course will not cover Tailwind CSS in comprehensive detail. Take your time to learn Tailwind CSS at your own pace.

Typography Utilities

To adjust the font size, you use the text- utility followed by the desired size. Tailwind provides a scale of predefined sizes.

<div>
  <p className="text-xs">Extra small text</p>
  <p className="text-sm">Small text</p>
  <p className="text-base">Base text size</p>
  <p className="text-lg">Large text</p>
  <p className="text-xl">Extra large text</p>
</div>

Extra small text

Small text

Base text size

Large text

Extra large text

Font weights are controlled with the font- utility.

<div>
  <p className="font-thin">Thin text</p>
  <p className="font-normal">Normal weight text</p>
  <p className="font-bold">Bold text</p>
</div>

Thin text

Normal weight text

Bold text

Text alignment is controlled with the text- utility followed by left, center, right, or justify.

<div>
  <p className="text-left">Left-aligned text</p>
  <p className="text-center">Center-aligned text</p>
  <p className="text-right">Right-aligned text</p>
  <p className="text-justify">Justified text</p>
</div>

Left-aligned text

Center-aligned text

Right-aligned text

Justified text

Remember that Tailwind's utility classes are designed to be used together, so you can combine any of these classes to style your text exactly the way you want. For example, <p className="text-lg font-bold">Bold large text</p> will create a paragraph of large, bold text.

Color Utilities

Text color is controlled with the text- utility followed by the color and shade you want to use. The color can be specified by name and the shade by number. Background color is controlled with the bg- utility followed by the color and shade you want to use. Border color is controlled with the border- utility followed by the color and shade you want to use.

<p className="text-red-300 bg-slate-800 border border-blue-800">
  Light red text on a dark slate background with a dark blue border.
</p>

Light red text on a dark slate background with a dark blue border.

Spacing Utilities

Tailwind provides a comprehensive set of spacing utilities that allow you to control the padding and margin of an element. The p- utility controls the padding of an element, while the m- utility controls the margin. The padding and margin can be specified on the x-axis, y-axis, or both. The size of the padding and margin can be specified by step number. In the spacing scale, a step of 1 is equivalent to 0.25rem (or 4px), a step of 2 is 0.5rem (or 8px), and so on.

<div>
  <p className="p-4 m-4 bg-slate-800">
    This paragraph has 16px of padding and margin.
  </p>
  <p className="px-4 py-2 m-8 bg-slate-800">
    This paragraph has 16px of padding on the x-axis, 8px of padding on the
    y-axis, and 32px of margin.
  </p>
</div>

This paragraph has 16px of padding and margin.

This paragraph has 16px of padding on the x-axis, 8px of padding on the y-axis, and 32px of margin.

React Skills Needed

Props (Properties)

Props (short for properties) are a way of passing data from parent components to child components in React. They are used to give components dynamic behavior. Props can include values of any data type, from strings to functions, and they are passed to components in the same way as HTML attributes.

Props are used for several reasons:

  • Customization: Props allow us to customize components. We can use the same component with different props to display different content or styles.
  • Reuse: With props, we can create reusable components. Instead of creating several similar components, we can create one and pass different props to it.
  • Data Flow: Props facilitate the unidirectional (top-down) data flow in React. This makes the code predictable and easier to debug.

Example:

function Greeting(props) {
  return <h1>Hello, {props.name}!</h1>;
}
 
export default function Page() {
  return <Greeting name="Mary" />;
}
 
// The output will be: "Hello, Mary!"

In this example, we pass the name prop to the Greeting component. The Greeting component then uses this prop to render the greeting.

We can also use destructuring:

function Greeting({ name }) {
  return <h1>Hello, {name}!</h1>;
}
 
export default function Page() {
  return <Greeting name="Mary" />;
}
 
// The output will be: "Hello, Mary!"

In the revised Greeting component, we're using JavaScript's destructuring assignment to pull out the name property from the props object right in the parameter list. This simplifies the code, making it easier to read and write.

Example:

Let's consider a scenario where we have a Profile component and this profile consists of a Photo component and a ProfileInfo component.


function Photo({ imageUrl, name }) {
  return (
    <div className="w-48 h-48 overflow-hidden rounded-full">
      <img src={imageUrl} alt={name} className="w-full h-full object-cover" />
    </div>
  );
}

function ProfileInfo({ name, bio }) {
  return (
    <div className="my-4">
      <h2 className="text-2xl font-bold text-gray-800">{name}</h2>
      <p className="mt-2 text-gray-600 text-justify">{bio}</p>
    </div>
  );
}

function Profile({ imageUrl, name, bio }) {
  return (
    <div className="bg-gray-100 p-8">
      <Photo imageUrl={imageUrl} name={name} />
      <ProfileInfo name={name} bio={bio} />
    </div>
  );
}

export default function Page() {
  const dog = {
    name: "Puddles",
    bio:
      "The sweet, eternally pensive pug who has mastered the art of melting hearts with his soulful eyes.",
    image:
      "https://images.pexels.com/photos/374908/pexels-photo-374908.jpeg?auto=compress&cs=tinysrgb&w=800&h=500&dpr=1",
  };
  
  return (
    <div className="bg-gray-300 p-10">
      <Profile name={dog.name} bio={dog.bio} imageUrl={dog.image} />
    </div>
  );
}

In this example, the Page component renders a Profile component and passes name, bio, and imageUrl props to it. The Profile component then passes these props to the Photo and ProfileInfo components. Photo uses imageUrl and name for the image source and alternative text, while ProfileInfo uses name and bio to display the user's name and short biography.

🗒️ Summary

  • Key JavaScript concepts for React include understanding Objects, JSON, destructuring, and template literals. Objects are standalone entities with properties and behavior, while JSON is a lightweight data-interchange format, containing only properties, no methods.
  • Destructuring allows unpacking of values from objects into distinct variables, simplifying code that accesses multiple properties of an object. Template literals provide a more readable and concise way to work with strings.
  • Tailwind CSS is a utility-first CSS framework providing low-level utility classes for building custom designs. It supports customization, responsiveness, and improves development speed.
  • Tailwind offers utilities for typography, color, and spacing, allowing control over font size, weight, text alignment, color of text, background, border, and control over padding and margin.
  • In React, props (short for properties) allow data to be passed from parent components to child components. They enable customization, component reusability, and facilitate unidirectional data flow.

📚 Knowledge Check

1. Which JavaScript concept allows developers to unpack values from objects into distinct variables?

Template Literals

Destructuring

Objects

JSON

2. In Tailwind CSS, what utility is used to control the font size?

font-

size-

text-

fs-

3. What is the term for data passed from parent components to child components in React?

States

Data

Props

Elements

4. In JavaScript, how do you access a property 'name' of an object 'user'?

user(name)

user[name]

user.name

name.user

5. What does JSON stand for in JavaScript?

JavaScript Object Notion

JavaScript Oriented Notation

JavaScript Object Notation

JavaScript Official Notation

6. In JavaScript ES6, what type of character encloses a template literal?

Double quotes

Single quotes

Parentheses

Backtick

7. How does Tailwind CSS allow for customization?

By offering pre-designed components

By providing low-level utility classes

By enforcing predetermined styles

By offering a set of high-level utility classes

8. In Tailwind CSS, how do you control the padding on the x-axis?

px-

x-

p-

padding-x-

9. What distinguishes JavaScript's object-oriented programming model from that of languages like Java or C++?

JavaScript uses classes for object-oriented programming

JavaScript uses a prototype-based inheritance model

JavaScript doesn't support object-oriented programming

JavaScript uses a class-based inheritance model

10. In a React component, what is the benefit of destructuring props in the function parameter list?

It simplifies the code, making it easier to read and write

It compiles the code into a more efficient format

It prevents errors from undeclared variables

It automatically checks the type of the props

🏃 Activity

The code below is not working. Fix the code so that it displays the correct output.


function Photo({ imageUrl, name }) {
  return (
    <div className="w-48 h-48 overflow-hidden rounded-full">
      <img src={imageUrl} alt={name} className="w-full h-full object-cover" />
    </div>
  );
}

function ProfileInfo({ name, bio }) {
  return (
    <div className="my-4">
      <h2 className="text-2xl font-bold text-gray-800">{name}</h2>
      <p className="mt-2 text-gray-600 text-justify">{bio}</p>
    </div>
  );
}

function Profile({ imageUrl, name, bio }) {
  return (
    <div className="bg-gray-100 p-8">
      <Photo imageUrl={imageUrl} name={name} />
      <ProfileInfo name={name} bio={bio} />
    </div>
  );
}

export default function Page() {
  const dog = {
    name: "Daisy",
    bio:
      "The Labrador Retriever whose infectious joy is rivaled only by the sunniest of summer days; she brightens every room she enters with her boundless energy and ever-wagging tail.",
    imageUrl:
      "https://images.pexels.com/photos/7752793/pexels-photo-7752793.jpeg?auto=compress&cs=tinysrgb&w=800&h=800&dpr=1",
  };
  
  return (
    <div className="bg-gray-300 p-10">
      <Profile dog={dog} />
    </div>
  );
}

💡 Hints

  1. The problem is only in the Page component.
  2. The Page component is passing the props to the Profile component incorrectly.

✅ Solution


function Photo({ imageUrl, name }) {
  return (
    <div className="w-48 h-48 overflow-hidden rounded-full">
      <img src={imageUrl} alt={name} className="w-full h-full object-cover" />
    </div>
  );
}

function ProfileInfo({ name, bio }) {
  return (
    <div className="my-4">
      <h2 className="text-2xl font-bold text-gray-800">{name}</h2>
      <p className="mt-2 text-gray-600 text-justify">{bio}</p>
    </div>
  );
}

function Profile({ imageUrl, name, bio }) {
  return (
    <div className="bg-gray-100 p-8">
      <Photo imageUrl={imageUrl} name={name} />
      <ProfileInfo name={name} bio={bio} />
    </div>
  );
}

export default function Page() {
  const dog = {
    name: "Daisy",
    bio:
      "The Labrador Retriever whose infectious joy is rivaled only by the sunniest of summer days; she brightens every room she enters with her boundless energy and ever-wagging tail.",
    imageUrl:
      "https://images.pexels.com/photos/7752793/pexels-photo-7752793.jpeg?auto=compress&cs=tinysrgb&w=800&h=800&dpr=1",
  };
  
  return (
    <div className="bg-gray-300 p-10">
      <Profile name={dog.name} bio={dog.bio} imageUrl={dog.imageUrl} />
    </div>
  );
}

🌐 Community Events App

As we progress in the course, the Community Events app will be updated with new features and functionality. Read through the code for Week 3 and try to understand how props are being used to pass data from parent components to child components. Which components are receiving props? What data is being passed to them? How are they using the props to render the UI?

📖 Further Reading