Stop Prop Drilling With React Context API

David Ang July 17, 2024

Do you have a similar code that you pass props around from parent to child components?


// src/App.js
import React from 'react';
import ProductImage from './components/ProductImage';
import ProductDescription from './components/ProductDescription';

const App = () => {
  const s3Settings = {
    bucketName: 'my-bucket',
    region: 'us-east-1',
  };

  const contentfulSettings = {
    spaceId: 'your-space-id',
    accessToken: 'your-access-token',
  };

  return (
    
      <ProductImage s3Settings={s3Settings} />
      <ProductDescription contentfulSettings={contentfulSettings} />
    
  );
};

export default App;

end

 


// src/components/ProductImage.js
import React from 'react';
import Image from './Image';

const ProductImage = ({ s3Settings }) => {
  return (
    
      <Image s3Settings={s3Settings} imagePath="example.jpg" />
    
  );
};

export default ProductImage;

end

 


// src/components/Image.js
import React from 'react';

const Image = ({ s3Settings, imagePath }) => {
  return (
    <img
      src={`https://${s3Settings.bucketName}.s3.${s3Settings.region}.amazonaws.com/${s3Settings.basePath}${imagePath}`}
      alt="Product"
    />
  );
};

export default Image;

end

 


// src/components/ProductDescription.js
import React from 'react';
import DescriptionFetcher from './DescriptionFetcher';

const ProductDescription = ({ contentfulSettings }) => {
  return (
    
      <DescriptionFetcher contentfulSettings={contentfulSettings} />
    
  );
};
export default ProductDescription;

end

 

If you find yourself passing in props around from parent to child and you are tired of it, you might benefit from using React Context API, which s a built-in feature of React.

Using React Context API, this becomes a provider that wraps our App component tree and becomes available in every components within.

 

Let’s create the Context:


// src/context/S3Context.js
import React, { createContext } from 'react';

const S3Context = createContext();

export const S3Provider = ({ children, settings }) => (
  <S3Context.Provider value={settings}>{children}</S3Context.Provider>
);

export default S3Context;

end

 

Similar with when creating our Contentful Context:


// src/context/ContentfulContext.js
import React, { createContext } from 'react';

const ContentfulContext = createContext();

export const ContentfulProvider = ({ children, settings }) => (
  <ContentfulContext.Provider value={settings}>{children}</ContentfulContext.Provider>
);

export default ContentfulContext;


end

 

Let's now use them as providers to our App:



// src/App.js
import React from 'react';
import ProductImage from './components/ProductImage';
import ProductDescription from './components/ProductDescription';
import { S3Provider } from './context/S3Context';
import { ContentfulProvider } from './context/ContentfulContext';

const App = () => {
  const s3Settings = {
    bucketName: 'my-bucket',
    region: 'us-east-1',
    basePath: 'images/',
  };

  const contentfulSettings = {
    spaceId: 'your-space-id',
    accessToken: 'your-access-token',
  };

  return (
    <S3Provider settings={s3Settings}>
      <ContentfulProvider settings={contentfulSettings}>
        
          
          
        
      </ContentfulProvider>
    </S3Provider>
  );
};

export default App;



end

 



// src/components/ProductImage.js
import React from 'react';
import Image from './Image';

const ProductImage = () => {
  return (
    
      
    
  );
};

export default ProductImage;


end

 

Notice that we don't pass the prop settings anymore, but we just retrieve them from the Context directly:


// src/components/Image.js
import React, { useContext } from 'react';
import S3Context from '../context/S3Context';

const Image = ({ imagePath }) => {
  const s3Settings = useContext(S3Context);

  return (
    <img
      src={`https://${s3Settings.bucketName}.s3.${s3Settings.region}.amazonaws.com/${s3Settings.basePath}${imagePath}`}
      alt="Product"
    />
  );
};

export default Image;

end

 

And we do similarly with retrieving the Contentful settings for the ProductDescription


// src/components/ProductDescription.js
import React, { useContext } from 'react';
import DescriptionFetcher from './DescriptionFetcher';
import ContentfulContext from '../context/ContentfulContext';

const ProductDescription = () => {
  const contentfulSettings = useContext(ContentfulContext);

  return (
    
      <DescriptionFetcher contentfulSettings={contentfulSettings} />
    
  );
};

export default ProductDescription;

end

 

This makes for a more cleaner code and you only retrieve the necessary settings where you need them.

If you need help migrating to React or refactoring your code into a cleaner, more maintainable approach, don't hesitate to contact us.

Ready to start your next project?