2
\$\begingroup\$

In one of my React components, I want to use props when creating a styled-component to determine which tag to use. Specifically I am looking for what's in props.headingLevel to decide which heading level to create. E.g. h1 or h2.

All of the styled-components examples show components being created outside of the React component class or function, however I have only got this working by creating the styled-component inside the function when props is available. I know you can use props inside of the styled-component via the props passed to the styled-component itself within the React function but at this point it's too late.

So my question is twofold. Is it acceptable to create styled-components inside React components functions? And, is there a better way to manage dynamic tags, e.g. creating a styled-component tag based on context or props passed?

Here is my component:

// Modules
import React from 'react';
import styled from 'styled-components';
import PropTypes from 'prop-types';

// Data
import styleData from '../../../data/styles';

const SupplementaryTitle = styled.span`
    display: block;
    text-align: right;
`

const Title = (props) => {
    const HeadingLevel = props.headingLevel ? props.headingLevel : 'h2';
    const StyledHeading = styled(HeadingLevel)`
        margin: 0;
        font-weight: ${styleData['typography_settings']['title'][props.typographySize]['weight']};
        font-size: ${styleData['typography_settings']['title'][props.typographySize]['size']};
        font-family: ${styleData['typography_settings']['title'][props.typographySize]['font']};
        line-height: ${styleData['typography_settings']['title'][props.typographySize]['line_height']};
    `

    const hasSupplementaryTitle = props.supplementaryTitle;

    if (hasSupplementaryTitle) {
        return (
            <StyledHeading>
                {props.children}
                <SupplementaryTitle>{props.supplementaryTitle}</SupplementaryTitle>
            </StyledHeading>
        )
    }

    return (
        <StyledHeading>{props.children}</StyledHeading>
    )
}

Title.propTypes = {
    children: PropTypes.string.isRequired,
    headingLevel: PropTypes.string,
    typographySize: PropTypes.oneOf([
        'small', 'medium', 'large'
    ]).isRequired,
    supplementaryTitle: PropTypes.string,
}

export default Title; 
\$\endgroup\$
  • 1
    \$\begingroup\$ If voting this question down, please specify your reasons why so that I can improve the question. \$\endgroup\$ – James Tudsbury Apr 25 '18 at 11:40
  • 1
    \$\begingroup\$ It's not my DV but this code is incomplete and there isn't really much to review... \$\endgroup\$ – t3chb0t Apr 25 '18 at 12:43
  • 1
    \$\begingroup\$ Updated to include full component code rather than snippet. Thanks for the input \$\endgroup\$ – James Tudsbury Apr 25 '18 at 15:49
1
\$\begingroup\$

You generally should not to nest your components. Though when the component is small enough, like a one-liner, it's fine to do so.

In your specific case, you can make use of withComponent method to change the tag name of an existing component:

const Heading = styled.h2`
    margin: 0;
    ...lots of css...
`;

const Title = (props) => {
    const StyledHeading = Heading.withComponent(props.headingLevel || "h2");
    ...
};

You'd still be defining the StyledHeader inside another component, but most of the code would live outside.

Additionally...

  • I noticed these long lines:

    font-weight: ${styleData['typography_settings']['title'][props.typographySize]['weight']};
    

    Looks like this styleData is some sort of theme. I suggest you look into built-in theming support of styled components before rolling your own.

  • The variable hasSupplementaryTitle can be eliminated. It's conventional to perform an existence check by just:

    if (props.supplementaryTitle) {
    
\$\endgroup\$
  • \$\begingroup\$ This is great feedback! Thanks. Definitely going to make use of withComponent and I don't know why I made the decision to have hasSupplementaryTitle. Over explaining myself in the code. I am going to look into theming more to see if it fits what I'm trying to do, styleData is basically a big object with all of my style values and the context they apply to, derived from the idea of design tokens. \$\endgroup\$ – James Tudsbury Apr 27 '18 at 7:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.