Search code examples
javascriptreactjsstyled-components

React — Passing props with styled-components


I just read in the styled-components documentation that the following is wrong and it will affect render times. If that is the case, how can I refactor the code and use the required props to create a dynamic style?

Thank you in advance.

Tab component

import React from 'react'
import styled from 'styled-components'

const Tab = ({ onClick, isSelected, children }) => {
    const TabWrapper = styled.li`
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 100px;
    margin: 1px;
    font-size: 3em;
    color: ${props => (isSelected ? `white` : `black`)};
    background-color: ${props => (isSelected ? `black` : `#C4C4C4`)};
    cursor: ${props => (isSelected ? 'default' : `pointer`)};
`

    return <TabWrapper onClick={onClick}>{children}</TabWrapper>
}


export default Tab

Solution

  • I believe what the documentation is saying is that you should avoid including your styles inside of the rendering component:

    DO THIS

    const StyledWrapper = styled.div`
      /* ... */
    `
    
    const Wrapper = ({ message }) => {
      return <StyledWrapper>{message}</StyledWrapper>
    }
    

    INSTEAD OF THIS

    const Wrapper = ({ message }) => {
      // WARNING: THIS IS VERY VERY BAD AND SLOW, DO NOT DO THIS!!!
      const StyledWrapper = styled.div`
        /* ... */
      `
    
      return <StyledWrapper>{message}</StyledWrapper>
    }
    

    Because what happens is when the component's Props changes, then the component will re-render and the style will regenerate. Therefore it makes sense to keep it separate.

    So if you read further on to the Adapting based on props section, they explain this:

    const Button = styled.button`
      /* Adapt the colours based on primary prop */
      background: ${props => props.primary ? "palevioletred" : "white"};
      color: ${props => props.primary ? "white" : "palevioletred"};
    
      font-size: 1em;
      margin: 1em;
      padding: 0.25em 1em;
      border: 2px solid palevioletred;
      border-radius: 3px;
    `;
    
    // class X extends React.Component {
    //  ...
    
    render(
      <div>
        <Button>Normal</Button>
        <Button primary>Primary</Button>
      </div>
    );
    
    // }
    

    this works because when you use the Button component in class X, it will know the props of class X without you having to tell it anything.

    For your scenario, I imagine the solution would be simply:

    const TabWrapper = styled.li`
      display: flex;
      align-items: center;
      justify-content: center;
      padding: 100px;
      margin: 1px;
      font-size: 3em;
      color: ${props => (props.isSelected ? `white` : `black`)};
      background-color: ${props => (props.isSelected ? `black` : `#C4C4C4`)};
      cursor: ${props => (props.isSelected ? 'default' : `pointer`)};
    `;
    
    const Tab = ({ onClick, isSelected, children }) => {
      return <TabWrapper onClick={onClick}>{children}</TabWrapper>
    }
    
    const X = <Tab onClick={() => console.log('clicked')} isSelected>Some Children</Tab>
    

    I haven't tested this at all, so please feel free to try it out and let me know if it works for you or whatever worked for you!