DEV Community

Cover image for What is prop drilling in React?
Code of Relevancy
Code of Relevancy

Posted on

What is prop drilling in React?

Yo ho, my hearties!!!

Prop drilling is the process of passing down data or state through multiple layers of a component hierarchy. By the way, it refers to the practice of passing data from a parent component to its children and then from the children to their own children and so on, until the data reaches the desired component that needs it..

Prop drilling can be a necessary and effective way to manage application state, it can also become a problem when the hierarchy of components becomes too deep or complex. This can lead to several issues. Let's say, code duplication, increased cognitive load and decreased maintainability..

With this article, we will discuss what are props, what prop drilling is and how it can impact your application. We will also explore some best practices for fixing prop drilling in your codebase..

The horizon's embrace, with the wind at our back and the sun on our face. Let's set sail into the depths of prop drilling..


What are Props in React?

Props (aka "Properties") are inputs to components. They are single values or objects containing a set of values that are passed to components on creation similar to HTML tag attributes. Here, the data is passed down from a parent component to a child component.

Props are read-only, meaning that they cannot be modified by the component itself. Used to customize the behavior or appearance of a component and they are passed down through the component tree in a unidirectional flow.

To show you what I mean..

function Snake(props) {
  return <h1>Hello, {props.name}!</h1>;
}

function DeadliestSnakes() {
  return (
    <div>
      <Snake name="Inland Taipan" />
      <Snake name="King Cobra" />
      <Snake name="Russell's Viper" />
      <Snake name="Black Mamba" />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Let's Understand the Prop Drilling

Prop drilling

Prop drilling occurs when a parent component passes data down to its children and then those children pass the same data down to their own children. This process can continue indefinitely. At the end, it's a long chain of component dependencies that can be difficult to manage and maintain.

Let's say you have a component that displays a list of blog posts. Each blog post has a title, date, author and content. In order to display this information, the parent component would need to pass down all of this data to its child components and those child components would need to pass it down to their own children and so on..

React DEV

As you can imagine, this process can become quite complex and unwieldy, especially as your application grows in size and complexity..


The Problems with Prop Drilling

Prop drilling can lead to several issues in your codebase, let's bring it on surface:

Code Duplication

When data needs to be passed down through multiple layers of components, it can result in code duplication. This occurs when the same data is passed down to multiple child components, even though they may not need all of it.

This can lead to bloated and redundant code which can be difficult to maintain and debug..

Increased Cognitive Load

Prop drilling can also increase the cognitive load on developers who need to understand and navigate the component hierarchy. As the number of components and layers increases, it can become difficult to keep track of which components are passing which data and where that data is being used.

This can make it more difficult to identify and fix bugs, as well as to make changes or updates to the codebase.

Decreased Maintainability

Prop drilling can lead to decreased maintainability over time. As your application grows in size and complexity, it can become more difficult to add new features or make changes to existing ones.

At the end, a codebase that is difficult to work with, which can lead to decreased productivity and increased frustration for developers.


A Hunt of Prop Drilling for Cleaner React Code

There are several ways that you can follow to fix prop drilling in your codebase. Let's bring it on surface:

Use a State Management Library

One of the most effective ways to fix prop drilling is to use a state management library, such as Redux or MobX. These libraries provide a centralized store for managing application state which can help to eliminate the need for prop drilling.

Instead of passing data down through multiple layers of components, you can simply connect each component to the store, and access the data that you need directly..

Use a State Management Library

You could refactor your code to use Redux:

  • Define a Redux store that holds the list of snakes:
import { createStore } from "redux";

const initialState = {
  snakes: [],
};

function rootReducer(state = initialState, action) {
  switch (action.type) {
    case "ADD_SNAKE":
      return {
        ...state,
        snakes: [...state.snakes, action.payload],
      };

    case "DELETE_SNAKE":
      return {
        ...state,
        snakes: state.snakes.filter((snake) => snake.id !== action.payload.id),
      };

    case "TRACK_SNAKE":
      return {
        ...state,
        snakes: state.snakes.map((snake) => {
          if (snake.id === action.payload.id) {
            return { ...snake, tracked: true };
          } else {
            return snake;
          }
        }),
      };
    default:
      return state;
  }
}

const store = createStore(rootReducer);
Enter fullscreen mode Exit fullscreen mode
  • Connect your components to the Redux store using the connect() function:
import { connect } from "react-redux";

function SnakeList(props) {
  const { snakes } = props;

  return (
    <ul>
      {snakes.map((snake) => (
        <Snake key={snake.id} snake={snake} />
      ))}
    </ul>
  );
}

function mapStateToProps(state) {
  return {
    snakes: state.snakes,
  };
}

export default connect(mapStateToProps)(SnakeList);
Enter fullscreen mode Exit fullscreen mode
  • Dispatch Redux actions to add, delete or track snakes:
function Snake({ snake, deleteSnake, trackSnake }) {

  const onTrackSnake = (id) => () => {
    trackSnake(id);
  };

  const onDeleteSnake = (id) => () => {
    deleteSnake(id);
  };

  return (
    <li>
      <input
        type="checkbox"
        checked={snake.tracked}
        onChange={onTrackSnake(snake.id)}
      />
      {snake.title} ({snake.tracked ? "Tracked" : "Not Tracked"})
      <button onClick={onDeleteSnake(snake.id)}>Delete</button>
    </li>
  );
}

function mapDispatchToProps(dispatch) {
  return {
    deleteSnake: (id) => dispatch({ type: "DELETE_SNAKE", payload: { id } }),
    trackSnake: (id) => dispatch({ type: "TRACK_SNAKE", payload: { id } }),
  };
}

export default connect(null, mapDispatchToProps)(Snake);
Enter fullscreen mode Exit fullscreen mode

Implement Context API

If you don't want to use a full fledged state management library, you can also consider using the Context API in React. This API provides a way to share data between components without the need for prop drilling.

Using the Context API, you can create a context object that holds the data that you need to share. You can then pass this context object down to the child components that need it, without having to pass it down through..

Implement Context API

You could use the Context API to share a snake object between components:

  • Create a snake context object:
import { createContext } from 'react';

export const SnakeContext = createContext({
  name: "Black Mamba",
  fangs: "6.5 mm",
  speed: "12.5 miles per hour",
  color: "#964B00", // dark brown
});
Enter fullscreen mode Exit fullscreen mode
  • Wrap your components in a provider component that provides the snake context:
function Jungle(props) {
  const snake = {
    name: "Black Mamba",
    fangs: "6.5 mm",
    speed: "12.5 miles per hour",
    color: "#964B00", // dark brown
  };

  return (
    <SnakeContext.Provider value={snake}>
      <Header />
      <Main />
    </SnakeContext.Provider>
  );
}
Enter fullscreen mode Exit fullscreen mode
  • Use the useContext() hook to access the snake object in your child components:
import { useContext } from "react";

function Header() {
  const { color } = useContext(SnakeContext);

  return (
    <header style={{ backgroundColor: color }}>
      <h1>Snake</h1>
    </header>
  );
}

function Main() {
  const { name, fangs, speed } = useContext(SnakeContext);

  return (
    <main>
      <p>
        Name: <span>{name}</span>
      </p>
      <p>
        Venom Fangs: <span>{fangs}</span>
      </p>
      <p>
        Speed: <span>{speed}</span>
      </p>
    </main>
  );
}
Enter fullscreen mode Exit fullscreen mode

Context is not limited to static values. If you pass a different value on the next render, React will update all the components reading it below!!! This is why context is often used in combination with state.

Use cases for context:

  1. Theming
  2. Current account
  3. Managing state

If some information is needed by distant components in different parts of the tree, it's a good indication that context will help you..


In the vast ocean of React development, prop drilling can be a stormy challenge to navigate through.. But my fellow captains, fear not! With the right tools and techniques, you can hoist the sails of clean and efficient code and steer your React application towards smoother waters⛵️🌊

Happy coding and smooth sailing on your React adventures!!! 🚀🌟🌈🏴‍☠️


❤ Motivation:

Blue Organic World Ocean Day.png


🍀Support

Please consider following and supporting us by subscribing to our channel. Your support is greatly appreciated and will help us continue creating content for you to enjoy. Thank you in advance for your support!

YouTube
Discord
GitHub

Dour Darcel #8740

Top comments (15)

Collapse
 
jon_snow789 profile image
Jon Snow • Edited

Thank you for sharing this. 👍💥💥

Need one help ...

How can i add sitemap in react js project

i add sitemap.xml file in public folder but google show this error

i add sitemap.xml file in public folder but google show this error

if i go to this url evstart.netlify.app/sitemap.xml

my sitemap is okay but problem on google search console

Please Help

Collapse
 
wizdomtek profile image
Christopher Glikpo ⭐ • Edited

Adding a sitemap to a ReactJS project is an important step for search engine optimization (SEO) and can help search engines better index your website. Here's a step-by-step guide on how to add a sitemap to your ReactJS project:

  1. Install the 'sitemap' package using npm or yarn.
    npm install sitemap
    or
    yarn add sitemap

  2. Create a sitemap file in your public directory. The file should be named sitemap.xml.

  3. Import the 'sitemap' package in your ReactJS component where you want to generate the sitemap.
    import { SitemapStream, streamToPromise } from 'sitemap';
    import { Readable } from 'stream';
    import { getServerSideProps } from 'next';
    import fs from 'fs';

  4. Create a function that will generate the sitemap data.
    async function generateSitemapXml() {
    const smStream = new SitemapStream({
    hostname: 'example.com',
    });
    const staticPages = [
    { url: '/', changefreq: 'daily', priority: 1 },
    { url: '/about', changefreq: 'monthly', priority: 0.8 },
    { url: '/contact', changefreq: 'weekly', priority: 0.5 },
    ];

staticPages.forEach((page) => {
smStream.write(page);
});

smStream.end();

const sitemapXml = await streamToPromise(Readable.from(smStream.toString())).then((data) =>
data.toString()
);

fs.writeFileSync('public/sitemap.xml', sitemapXml);
}
Call the function in the getServerSideProps method.
export async function getServerSideProps(context) {
await generateSitemapXml();

return {
props: {},
};
}
Finally, add the sitemap.xml file to your robots.txt file.
Sitemap: example.com/sitemap.xml
Once you have completed these steps, your sitemap should be generated automatically whenever your ReactJS project is built, and search engines will be able to crawl and index your site more effectively.

Collapse
 
jon_snow789 profile image
Jon Snow

i add this code

const fs = require('fs');
const { Sitemap } = require('sitemap');

const pages = [
  { url: '/', changefreq: 'daily', priority: 1.0 },
  { url: '/about', changefreq: 'weekly', priority: 0.8 },
  { url: '/contact', changefreq: 'monthly', priority: 0.5 },
];

const sitemap = new Sitemap({ pages });
const xml = sitemap.toXML();

fs.writeFile('./public/sitemap.xml', xml, (err) => {
  if (err) throw err;
  console.log('Sitemap generated successfully!');
});
Enter fullscreen mode Exit fullscreen mode

Should i add anything else

Collapse
 
codeofrelevancy profile image
Code of Relevancy • Edited

make sure to remove extra spaces with all the tags:

<loc> https://evstart.netlify.app/scooter/ather-450x-gen3 </loc>
Enter fullscreen mode Exit fullscreen mode

it should be like this

<loc>https://evstart.netlify.app/scooter/ather-450x-gen3</loc>
Enter fullscreen mode Exit fullscreen mode
Collapse
 
jon_snow789 profile image
Jon Snow

thank you

Collapse
 
codeofrelevancy profile image
Code of Relevancy

update this tag:
lastmod instead of Lastmod

Collapse
 
jon_snow789 profile image
Jon Snow

i update my sitemap but still getting same error

i update my sitemap but still getting same error

Collapse
 
ozzyogkush profile image
Derek Rosenzweig • Edited

There's a point where you have to layer your application code and be strict about what is responsible for domain, data access, connecting all your state for top-level views, and what (and how) data gets passed down to various children.

Sometimes you need intermediate connectors to connect a specific feature's state with overall app state. Separate those concerns and be consistent and you'll have a better time overall.

Lastly, the lower in the chain your components get, the dumber they should be. And by this I mean, if you have a folder for a view that contains a connector and several components used as children in the connector, those children don't necessarily have to know anything about the "what" or "why" of the values of the data coming into them - you can name them and use them only in the context of the child component. EG, instead of passing a function for an action creator from the connector called sendTheDataToTheBackendAndShowAGrowlerWhenDone with the same name to a deep child that's run when the user clicks on some button in the child, , call the prop in the child onSendButtonClick. Cleaner interfaces = easier to understand the context of your data in any given component. Deeply nested components means more general prop names.

This means not blindly passing props down - you are using differently named props, so you end up with much clearer boundaries for each interface. TypeScript really helps here because if you end up doing that, you'll get type errors. You can use the spread operator to name rest something useful like childXProps and apply those to the appropriate child; or if using different prop names, applying those manually.

Collapse
 
ant_f_dev profile image
Anthony Fung

Thanks for sharing.

I remember coming across this issue when learning React a while ago. I was transitioning from Silverlight and jumped straight into a project where the project lead had chosen Redux for state management. I was still trying to get my head around (standard) React and initially got so confused by container components!

Most of the projects I work on are Angular based nowadays, so my React knowledge is a bit stale. I seem to remember that the context was use-at-your-own-risk at the time (which admittedly was a long time ago) as things were still maturing and subject to API changes. Is the context API stable now in more recent versions?

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Yes I think so.. Context API is more stable than before.

Collapse
 
satriopamungkas profile image
Muhammad Raihan Satrio Putra Pamungkas

Nice share, this reminds me of my previous project. I had encountered this anti-pattern when the Atomic Design methodology was being used on my React project. It's likely that you will encounter a similar issue when using Atomic Design.

This means that you are separating components from the largest to the smallest one. It consists of 4 to 5 level components, therefore we have to pass the data from the top-level parent all the way down to the bottom level.

If we use a common pattern, there will be a lot of unused props. This issue is challenging, and I prefer to use the Context API or other state management tools approach.

Collapse
 
alisinayousofi profile image
Ali Sina Yousofi

Thanks Code of relevancy.
Using redux will make things easier.

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thank you reading

Collapse
 
khatrinitesh profile image
Nitesh Khatri

It is good example and best implement.

Collapse
 
codeofrelevancy profile image
Code of Relevancy

Thanks