Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

styled-component .attrs - React does not recognize prop

I am trying to pass down a prop into my styled component. It works as expected, but React throws the known 'Unknown Prop' error.

I tried to use the spread operator at numerous places but neither did work.

The styled component I want to pass down the prop to:

const StyledBackgroundImage = styled(BackgroundImage).attrs(({minHeight}) => ({
  minHeight: minHeight || "60vh",
}))`
  min-height: ${({minHeight}) => minHeight};
  /* ...  */
`;

The parent component:

const ImageWithText = ({imageData, minHeight, children}) => {
  return (
    <StyledBackgroundImage 
    Tag="div"
    backgroundColor={'#000000'}
    fluid={imageData}
    minHeight={minHeight}
    >
        {children}
    </StyledBackgroundImage>
  )
}

And how I use it on the page:

<ImageWithText imageData={data.headerBackgroundImage.childImageSharp.fluid} minHeight='50vh'>

I expect it to work, which does, but not without the following error:

Warning: React does not recognize the `minHeight` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `minheight` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
    in div (created by BackgroundImage)
    in BackgroundImage (created by Context.Consumer)
    in StyledComponent (created by ImageWithText__StyledBackgroundImage)
    in ImageWithText__StyledBackgroundImage (at ImageWithText.js:32)
    in ImageWithText (at pages/index.js:20)
    in section (created by Context.Consumer)
    in StyledComponent (created by LayoutComponents__Section)
    in LayoutComponents__Section (at pages/index.js:19)
    in main (at layout.js:10)
    in Layout (at pages/index.js:17)
    in IndexPage (created by HotExportedIndexPage)
    in AppContainer (created by HotExportedIndexPage)
    in HotExportedIndexPage (created by PageRenderer)
    in PageRenderer (at json-store.js:93)
    in JSONStore (at root.js:51)
    in RouteHandler (at root.js:73)
    in div (created by FocusHandlerImpl)
    in FocusHandlerImpl (created by Context.Consumer)
    in FocusHandler (created by RouterImpl)
    in RouterImpl (created by Context.Consumer)
    in Location (created by Context.Consumer)
    in Router (created by EnsureResources)
    in ScrollContext (at root.js:64)
    in RouteUpdates (at root.js:63)
    in EnsureResources (at root.js:61)
    in LocationHandler (at root.js:119)
    in LocationProvider (created by Context.Consumer)
    in Location (at root.js:118)
    in Root (at root.js:127)
    in _default (at app.js:65)
like image 289
Steinroe Avatar asked Aug 21 '19 07:08

Steinroe


2 Answers

Update: Use transient props

With the release 5.1.0 you can use transient props. This way you do not need an extra wrapper i.e. unnecessary code is reduced:

Transient props are a new pattern to pass props that are explicitly consumed only by styled components and are not meant to be passed down to deeper component layers. Here's how you use them:

const Comp = styled.div`
  color: ${props => props.$fg || 'black'};
`;

render(<Comp $fg="red">I'm red!</Comp>);

Note the dollar sign ($) prefix on the prop; this marks it as transient and styled-components knows not to add it to the rendered DOM element or pass it further down the component hierarchy.

The new answer should be:

Component:

<ImageWithText 
  $imageData={data.headerBackgroundImage.childImageSharp.fluid} // notice the '$'
  minHeight='50vh'>

Declaration of styled component:

const StyledBackgroundImage = styled(BackgroundImage).attrs(({$minHeight}) => ({
  minHeight: minHeight || "60vh",
}))`
  min-height: ${({$minHeight}) => $minHeight}; // notice the '$' before the prop name
  /* ...  */
`;
like image 137
EliteRaceElephant Avatar answered Oct 15 '22 20:10

EliteRaceElephant


To prevent components that pass all props to a DOM element, create a wrap of your component but do not pass your custom props to the children component via "Object destructuring". In this way, you can style the wrapped component and styled-components can access to the prop but the children will not have custom properties, and your warning will disappear.

import React from 'react';
import styled from 'styled-components';
import Typography from '@material-ui/core/Typography'

const WrappedTypography = ({ maxWidth, ...props }) => {
  return <Typography {...props} />
}

const Text = styled(WrappedTypography) `
  ${({ maxWidth }) => maxWidth ? `max-width: ${maxWidth}` : null}
`
export default Text;

You can learn more about deestructuring at Mozilla docs.

like image 45
kartojal Avatar answered Oct 15 '22 20:10

kartojal