Mastering the React Props

Mastering the React Props

Welcome back to our React journey! In our last blog, we discovered the magic of props - how they transform components into dynamic building blocks. Today, we're taking it up a notch.

How to use the spread operator (...) in React?

Stumbled upon three dots or an ellipsis in JavaScript and wondered what on earth it means?

What is the Spread Operator?

The spread operator allows you to expand elements from one array, object, or other iterable into another.

This can be useful when copying arrays or objects, or when working with several arguments or properties.

In the context of React, the spread operator can be used to spread an object of props onto an element in a React component, making it easier to pass down props to child components.

Examples of the Spread Operator

The spread operator is denoted by three dots: ....

Let’s have a look at some use cases for the spread syntax.

Copy an array with spread

Creating a shallow copy of an array becomes a breeze with the spread operator.

A shallow copy of an array empowers you to manipulate the data within the duplicate without impacting the original array.

const airbnbExperiences = [
    "Life lessons",
    "Learn wedding photography", 
    "Group mountain biking"
];

// creating a shallow copy of the array using
// the spread syntax

const airbnbExperiences = [...airbnbExperiences];

console.log(airbnbExperiences); 

/* result ["Life lessons", "Learn wedding photography", "Group mountain biking"] */

Copying an array isn’t something you need to do every day, so let’s build on this syntax with the next example!

Joins arrays with spread

Another situation where the spread operator comes in handy is when we need to concatenate two arrays or more arrays.

const airbnbExperiences = [
    "Life lessons",
    "Learn wedding photography", 
    "Group mountain biking"
];

const moreAirbnbExperiences = [
  "Art workshop",
  "Guided tours",
  "Outdoor adventure",
  ];

// concatenating the two arrays into a new one:

const allAirbnbExperiences = 
  [...airbnbExperiences, 
   ...moreAirbnbExperiences];

console.log(allAirbnbExperiences);

/* result
["Walk in the woods", 
 "Local cooking class", 
 "Guided tour of the city", 
 "Night diving", 
 "Volcano hike", 
 "Language class"]
*/

Combine objects with spread

Likewise, we possess the capability to spread the elements of an object, while also seamlessly incorporating fresh elements through the use of the spread syntax. This feature holds immense power and is destined to become a trusted ally in your routine JavaScript tasks.

const airbnbListings = {
  exp1: {
    host: "Katie",
    exp: "Life lessons",
    location: "Online",
  },
  exp2: {
    host: "Zaferes",
    exp: "Local Cooking Class",
    location: "Italy",
  }
};

const updatedAirbnbListings = {
  ...airbnbExperiences,
  exp3: {
    host: "Naomi",
    exp: "Group mountain biking",
    location: "Norway",
  },
};

console.log(updatedAirbnbListings);

/* result
{
   exp1: {
    host: "Katie",
    exp: "Life lessons",
    location: "Online",
       },
   exp2: {
    host: "Zaferes",
    exp: "Local Cooking Class",
    location: "Italy",
       },
   exp3: {
    host: "Naomi",
    exp: "Group mountain biking",
    location: "Norway"
       }
  }
*/

Spread an array to function arguments

Math.min is an anomaly in its behavior. One might naturally assume that it accepts an array of values, yet it functions by anticipating an unlimited series of Number arguments.

Math.min([14, 34, 54, 114]) // ❌
Math.min(14, 34, 54, 114) // ✅

What would you do if you had an array but needed to find the minimum value?

In this situation, Spread comes to the rescue!

We can also use the spread syntax to spread the elements of an array as separate arguments when calling a function:

const min = [14, 34, 54, 114]

console.log (Math.min(...min)) // 14

How to use the spread operator to spread props in a React component

The spread syntax can be very useful in React components, especially when dealing with props. To thoroughly understand this section, you need to be familiar with React components, their parent-child relationships, and props. If you struggle to understand props or you simply want to revise your concept, you can always revisit the Understanding the React Props.

Props function as objects, and as we've just observed, the spread operator allows an object to be effortlessly expanded into its key-value pairs.

Let’s build a small app to display data about Airbnb Experiences.

We start by building a component called Card, which receives props. In this case, prop is an object containing information about the user:

  • experience

  • location

  • price

import React from "react"

export default function Card(props) {
    let badgeText
    if (props.openSpots === 0) {
        badgeText = "SOLD OUT"
    } else if (props.location === "Online") {
        badgeText = "ONLINE"
    }

    return (
        <div className="card">
            {
                badgeText && 
                <div className="card--badge">{badgeText}</div>
            }
            <img 
                src={`../images/${props.coverImg}`} 
                className="card--image" 
            />
            <div className="card--stats">
                <img src="../images/star.png" className="card--star" />
                <span>{props.stats.rating}</span>
                <span className="gray">({props.stats.reviewCount}) • </span>
                <span className="gray">{props.location}</span>
            </div>
            <p className="card--title">{props.title}</p>
            <p className="card--price">
                <span className="bold">From ${props.price}</span> / person
            </p>
        </div>
    )
}

In our App, we can render the Card component. Now, how do we pass the props to the component? We have two chances:

--> Declaring the props one by one

//Case 1: declare every prop

import React from "react"
import Card from "./components/Card"

const props = {
    id: 1,
    title: "Life Lessons with Katie",
    description: "I will share with you what I call \"Positively Impactful Moments of Disappointment.\" Throughout my career, many of my highest moments only came after setbacks and losses. But learning from those difficult moments is what gave me the ability to rise above them and reach my goals.",
    price: 136,
    coverImg: "katie.png",
    stats: {
        rating: 5.0,
        reviewCount: 6
        },
    location: "Online",
    openSpots: 0,
};

export default function App() {
  return (
    <div>
      <Card
        key={item.id}
        img={item.coverImg}
        rating={item.stats.rating}
        reviewCount={item.stats.reviewCount}
        location={item.location}
        title={item.title}
        price={item.price}
        openSpots={item.openSpots}
      />
    </div>
  );
}

--> Using the spread syntax to pass the whole props object in one easy go!

//Case 2: using the spread operator

import React from "react"
import Card from "./components/Card"

const props = {
    id: 1,
    title: "Life Lessons with Katie",
    description: "I will share with you what I call \"Positively Impactful Moments of Disappointment.\" Throughout my career, many of my highest moments only came after setbacks and losses. But learning from those difficult moments is what gave me the ability to rise above them and reach my goals.",
    price: 136,
    coverImg: "katie.png",
    stats: {
        rating: 5.0,
        reviewCount: 6
        },
    location: "Online",
    openSpots: 0,
};

export default function App() {
    const cards = data.map(item => {
        return (
            <Card
                key={item.id}
                {...item}
            />
        )
    })

What we render is the same.

Understanding Default Props

What are Default Props?

Default props are pre-defined values that a React component uses when a prop is not explicitly provided during its usage. These values act as fallbacks, ensuring that your component remains functional and visually appealing, even when specific props are omitted.

Imagine you're building a UI library or a set of components for a website. Users of your components might not always provide all the props, especially optional ones. This is where default props become indispensable, as they maintain the component's integrity in various usage scenarios.

Suppose you're crafting an ArticleCard component that displays articles with images. Some articles might not have images, so setting a default placeholder image is a smart move.

function ArticleCard(props) {
  return (
    <div className="article-card">
      <img src={props.image} alt="Article" />
      <h2>{props.title}</h2>
      <p>{props.content}</p>
    </div>
  );
}

ArticleCard.defaultProps = {
  image: 'placeholder-image.jpg', // Default image if not provided
};

Setting Default Props in a Class Component

In class components, default props are defined using the defaultProps static property. This property is a part of the class itself and holds an object where you can specify default values for the props.

class MyComponent extends React.Component {
  // ...
}

MyComponent.defaultProps = {
  message: 'Setting Default Props in class component',
};

Leveraging Default Props in Functional Components

In functional components, default props are set using the defaultProps property attached to the function component itself. This property is assigned an object with the default values for the props.

function MyFunctionalComponent(props) {
  // ...
}

MyFunctionalComponent.defaultProps = {
  message: 'Leveraging Default Props in Functional Components',
};

In essence, the process of defining and applying default props is quite similar between class components and functional components. The key distinction is in how you associate the defaultProps property with the component definition - whether as a static property within a class component or directly with the function component in the case of functional components.

Prop Drilling and Context API: Navigating Data Flow in React

What is Prop Drilling?

Prop drilling is a situation where data is passed from one component through multiple interdependent components until you get to the component where the data is needed.

While it's a straightforward way to share data, it can become cumbersome and lead to unnecessary complexity as the application grows. Hence the need for global state management.

Here's an illustration of prop drilling to help you understand:

When it comes to evading prop drilling and efficiently managing your data at a global scale, the React Context API steps in as a rapid solution. This approach empowers you to maintain your data flow globally without resorting to the intricacies of extensive third-party state management tools like Redux and MobX.

Introducing the React Context API

React context is an inherent feature that leverages the useContext hook to facilitate seamless data sharing among components.

Consider the scenario of transmitting authenticated user data from a parent component to a deeply nested child component. In the traditional method, passing this data through numerous intermediate components becomes unwieldy.

A better approach to doing this is using React context to handle the data.

How to Use the React Context API

How to Create Context

useContext is a built-in hook in React. You can start using the context API by importing the createContext function from React like this:

import {createContext} from ‘react’;
const AuthContext = createContext();

Here, we initialized our context and named it AuthContext. The next step is to provide the context.

How to provide the context to the components that need it

The context API uses a provider to pass data to its child components. You will have to warp all components with a provider component.

<AuthContext.Provider value={...}>
    <ParentComponent/>
<AuthContext.Provider>

As seen above, the Provider component has a value prop. The value of the context can either be updated or set using the value prop. In this taken case, we will be setting the value prop to the name of our authenticated user.

import React from ‘react’;

function App() {

    const username = “katie”

    return(
        <AuthContext.Provider value={username}>
            <Dashboard/>
        <AuthContext.Provider>
    )
}

export default App;

Now will see how to use the context.

How to consume the context

We can consume the context by using the useContext hook. You can access your context in any component you want without passing data through the nested components.

import { useContext } from ‘react’;

const Profile = () => {

    const value = useContext(AuthContext);

    return (
        <div>
            {value}
        </div>
    )
}

export default Profile

Note that the React context API is not a replacement for a global state management tool like Redux and MobX.

I hope this blog was helpful to you and the upcoming blog will cover the concepts of React Hooks.

Thanks for Reading.