Kris Selbekk
Kris Selbekk

Reputation: 7624

Stopping props to be passed on to child with styled-components

I'm playing with styled-components for the first time, and I'm having an issue with ´react` passing on props only used by the styled component itself.

Here's my component:

import { Link } from 'react-router-dom';

const CtaButton = styled(Link)`
  background: ${props => props.primary ? 'red' : 'yellow'}
  color: white;
  display: inline-block;
  padding: 0.5em 1em;
`;

When I invoke this with the primary prop, I get a warning from react that I'm applying the primary prop to the <a /> element. I understand why this happens - but how can I stop it?

I can of course create a wrapper around react-router's Link component that strips this prop - but that would be kind of clumsy. I'm sure it's just me not being a pro in this lib's API yet - so can somebody please point me in the right direction?

For some reason I don't have this issue when I create DOM component directly (styled.a for instance).

Upvotes: 2

Views: 1342

Answers (1)

Kris Selbekk
Kris Selbekk

Reputation: 7624

Looks like this is a known limitation of styled-components. The reason this doesn't work is because the library strips props when applied to DOM-elements (based on a white list). This can't really be done the same way with components, since a random component does not really have a predictable API.

While the authors and contributors are fixing this, this is the work-around I came up with:

import React from 'react';
import { Link } from 'react-router-dom';

const StyledLink = ({ primary, ...rest }) => <Link {...rest} />;

export const CtaButton = styled(StyledLink)`
  background: ${props => props.primary ? 'red' : 'yellow'}
  color: white;
  display: inline-block;
  padding: 0.5em 1em;
`;

In other words, wrapping the component with another component that strips whatever styled component specific props and then re-applies the remaining props. It's not pretty, but as far as I can see it's the simplest solution there is.

You could also create a HOC that would do this for you:

const withStrippedProps = propsToRemove => TargetComponent => (props) => {
  const strippedProps = Object.entries(props)
    .filter(([key]) => !propsToRemove.includes(key))
    .reduce((stripped, [key, value]) => ({ ...stripped, [key]: value }), {});
  return <TargetComponent {...strippedProps} />
};

const StyledLink = withoutProps(['primary'])(Link);
const CtaButton = styled(StyledLink)`
  // css goes here
`;

I'm accepting this as the answer for now, but if there are any other approaches that doesn't create a wrapper component / function like this, I'll be open to accepting another answer.

Upvotes: 3

Related Questions