Introduction  props in React

Introduction props in React

All you need to know about props in React, in a single blog

What exactly is a prop?

A prop is a shorthand name for ‘properties.’ It is used to pass data from a parent component to its children components. A child component cannot modify the prop; props are read-only.

Props are similar to attributes in HTML DOM elements, the only difference being, props are defined by the developer (contrary to pre-defined HTML attributes).

//a simple HTML element with 'className', 'id' attributes
<div className='container' id='home'>...</div>

//a parent element passing data to its child component via props
<div className='parent'>
    <ChildComponent
        name='Christen'
        location='NYC'
        age={34}
    />
</div>

In the above JSX code, the ChildComponent (a custom React component) is passed data via three props: name, location and age.

Now let us understand how the ChildComponent access the data passed to it via props.

import React from "react";
/* NOTE: 'props' is just a parameter name, you can call it anything you wish (be it 'oranges' or 'apples', really doesn't matter!). But naming it 'props' is a React best-practice. */
export default function ChildComponent(props) {
  return (
    <div classNanme='child'>
      <h1 className="name">{props.name}</h1>
      <h2 className="location">{props.location}</h2>
      <h3 className="age">{props.age}</h3>
    </div>
  );
}

The ChildComponent receives a props parameter as a JavaScript object. This object contains all the data passed to ChildComponent. The props object will essentially look like this:

//props are received as JavaScript Objects by the components
const props = {
    name: 'Christen',
    age: 34,
    location: 'NYC'
}

Once the data is received, we can use it anywhere in the ChildComponent.

Best practices while working with props

Development is all about making our code clean and concise. We can use several JS techniques (thanks to ES6 and above) to write much shorter code while working with props.

JavaScript Object-destructuring

Since props are received as a JavaScript object, they can be destructured to avoid writing props. everywhere. This makes it easier to read as well as saves time. Learn more about JS object-destructuring here.

import React from "react";

export default function ChildComponent(props) {
  const {name,location,age} = props; //object destructuring
  return (
    <div className='child'>
      <h1 className="name">{name}</h1>
      <h2 className="location">{location}</h2>
      <h3 className="age">{age}</h3>
    </div>
  );
}

Passing an object to a prop

Instead of passing each data field separately to the child component, we can create an object consisting of all the data fields and pass that to the child component as a prop.

import React from "react";

export default function ParentComponent(props) {
   //declaring an object named - info
   const info = {
        name : 'Christen',
        location : 'NYC',
        age : 34
   };

   return (
        <div className='parent'>
            <ChildComponent
                item = {info}
            />
        </div>
   );
};
/* 
NOTE: 'item' is the name of the prop being passed to the child-component while 'info' is the actual object. 
For simplicity, you can rename 'item' --> 'info' but make sure to rename it inside the child-component as well. 
*/

This can be a little tricky to understand. Let us dig deeper into the above code.

We know that the child component receives a prop as an object, and we have passed an object as a prop. So effectively, the props object will contain yet another object which contains the data fields. The props object will look like this:

const props = {
    item : {
        name: 'Christen',
        location: 'NYC',
        age: 34
    }
};

So now, we extract the complete item Object while destructuring the prop. The child component will access the prop in the following manner:

import React from "react";

export default function ChildComponent(props) {
  const {item} = props; //extract the item-object
  const {name,age,location} = item;  //extract data fields
  /*
       Both the above steps can be combined as follows:-
       const {name,age,location} = props.item;
  */
  return (
    <div className='child'>
      <h1 className="name">{name}</h1>
      <h2 className="location">{location}</h2>
      <h3 className="age">{age}</h3>
    </div>
  );
}

Passing functions as props

Suppose multiple components require the same function to be executed whenever an event occurs in them (say, a button is clicked). What would you do? You do not wish to rewrite the same function in each one.

You would think of creating the function in the parent component and set an onClick event listener in all the children components. But that doesn't work.

Event listeners, such as onClick, onChange, and others, do not work with custom react components as these are not native DOM elements. So if we want to add event listeners to a custom React component, we must pass the function as a prop.

/* App.jsx */
import React from 'react'
import Homepage from './Homepage'
export default function App() {
   function myFunc(){
        console.log('myFunc function called'); //function body
   };
   return (
        <div className='app'>
            <Homepage handleClick={myFunc} />
        </div>
   );
};

In the App component, myFunc function is passed as a prop (with name handleClick) to the Homepage component.

/* Homepage.jsx */
import React from 'react'

export default function Homepage(props) {
   const {handleClick} = props;
   return (
        <button onClick={handleClick}>CLICK</button>
   );
};

Let us understand the flow of events in the above two code files.

  1. When the user clicks the button, the onClick event is triggered, which invokes (or calls) the handleClick prop.

  2. handleClick is nothing but the prop's name that finally triggers the myFunc function.

Passing a state as a prop

Passing a React state as a prop can be helpful when the data displayed on the screen is subject to change when the user interacts with the website.

Before proceeding, we need to keep some crucial points in mind:

  1. React re-renders the whole component whenever the state changes.

  2. Props passed to a component are read-only; the component (to which it is passed) cannot modify them. This is also true for states passed as a prop.

  3. In React, data flows in the top-to-bottom direction. This means that a component does not know that a sibling component exists.

    In the code shown below, the Header and the Homepage components are not aware of each other. The Header (or Homepage) component doesn't even know about its grandparent component (i.e., any component above the App component).

     import React from 'react'
     export default function App(){
         return (
             <Header/>
             <Homepage/>
         );
     }
    

Giving child component the ability to change the state

As we learned earlier, a child component cannot change the state passed to it as a prop. But there are instances where we would want to change the state somehow.

This can be done by passing the useState setter-function as a prop to the child component (We now know how to pass a function as a prop). Let's understand this with an example.

Consider creating a website with just one card that displays a shoe with some details. You want to add that item to the wishlist whenever the 'add to wishlist' button is clicked. A separate wishlist component renders the shoe brand name if the item is marked to the wishlist; otherwise, not.

Let's get there step by step:

  1. Create a state called favorite which marks whether the item is marked to the wishlist. Set it to false initially.

  2. Create a function (addToFav) that toggles the state whenever it is invoked.

  3. Pass the shoe object and the addToFav function to the Shoecard component.

  4. Pass the shoe object and the favorite state to the Wishlist component.

/* App.jsx */
import React, { useState } from "react";
import "./App.css";
import Wishlist from "./Wishlist";
import ShoeCard from "./ShoeCard";

const shoe = {
  brand: "Nike",
  price: 30,
  size: "Medium",
};

export default function App() {
  const [favorite, setFavorite] = useState(false);
  // toggles the state named 'favorite'
  function addToFav() {
    setFavorite((fav) => !fav);
  }

  return (
    <div className="app">
      <div className="container">
        <ShoeCard shoe={shoe} handleClick={addToFav} />
      </div>
      <Wishlist favorite={favorite} shoe={shoe} />
    </div>
  );
}
/* For simplicity, the name of the prop and the object/state is kept identical */
  1. In the Shoecard component, access the shoe and the handleClick prop. Add an onClick event listener on the button, which triggers handleClick prop when clicked. The handleClick prop will invoke the addToFav function eventually.
/* Shoecard.jsx */
import React from "react";

export default function ShoeCard(props) {
  const { brand, size, price } = props.shoe;
  const { handleClick } = props;
  return (
    <div className="card">
      <h2>{brand}</h2>
      <p>{size}</p>
      <p>{price}</p>
      <button onClick={handleClick}>Add to Wishlist</button>
    </div>
  );
}
  1. In the Wishlist component, access shoe and favorite via props. Now, using the ternary conditional operator, display the shoe brand if the favorite state is true.
/* Wishlist.jsx */
import React from "react";

export default function Cart(props) {
  const { shoe, favorite } = props;
  return (
    <div className="wishlist">
      {favorite ? <div>{shoe.brand}</div> : "No items in wishlist"}
    </div>
  );
}

Let's understand the flow of events:

  • The 'Add to Wishlist' button is clicked, and handleClick prop is triggered.

  • The handleClick prop triggers the addToFav function.

  • The addToFav function uses the state setter-function to change the favorite state to true.

  • Once the state is changed, React re-renders the App, Shoecard, and Wishlist components.

  • While re-rendering the Wishlist component, the favorite prop is now true (initially false), and the shoe brand name eventually appears on the screen.

Summary

In this blog, we learned the meaning and importance of props in React. We discussed different ways to use props through examples and some of the best practices while using props in React. In the end, we discussed some advanced concepts of passing states as props and how to change them.

Conclusion

I hope you found this blog helpful and knowledgeable. Considering the fact that "The best way to learn is to teach," I share whatever I learn through blogs. Do give your opinions in the comments section.

Keep Learning !!