'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 1:[1]
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!
Solution 2:[2]
You can pass an argument with Typescript as follows:
<StyledPaper open={open} />    
...
const StyledPaper = styled(Paper)<{ open: boolean }>`
   top: ${p => (p.open ? 0 : 100)}%;
`;
Solution 3:[3]
Another way to do it would be
const StyledDiv = styled.div.attrs((props: {color: string}) => props)`
    width: 100%;
    height: 100%;
    background-color: ${(props) => props.color};
`
//...
render() {
    return (
        <StyledDiv color="black">content...</StyledDiv>
    );
}
This way you are type-safe in terms of the props you want to send into the styled component. (Good when coding in Typescript)
Solution 4:[4]
For a more simple example with functional components:
Suppose you have an arrow like polygon and you need 2 of them pointing in different directions. So you can pass the rotate value by props
               <Arrow rotates='none'/>
          
               <Arrow rotates='180deg'/>
Then in the Component Arrow you have to pass the props like normal component to the styled component but in the styled component you have to use it like props:
    import React from 'react';
    import styled from "@emotion/styled";
    const ArrowStyled = styled.div`
      background-color: rgba(255,255,255,0.9);
      width: 24px;
      height: 30px;
      clip-path: polygon(56% 40%,40% 50%,55% 63%,55% 93%,0% 50%,56% 9%);
      transform: rotate(${props => props.rotates});
     `
const Arrow = ({rotates}) => {
    return (
       <ArrowStyled rotates={rotates}/>
    );
}
export default Arrow;
           
Solution 5:[5]
If you're using Typescript create an interface inside your styles file! Otherwise, you won't be able to access props in your CSS
import styled from 'styled-components'
interface StyledLiProps{
  selected: boolean
}
export const TabWrapper = styled.li`
    
// styles ...
color: ${props => (selected ? `white` : `black`)};
background-color: ${props => (selected ? `black` : `#C4C4C4`)};
`
And don`t forget to declare the props you want to use in your CSS inside your JSX
interface TabProps{
  text: string;
}
const Tab = ({ text }: TabProps) => {
 
//...
  
return <TabWrapper selected={isSelected} onClick={() => updateTab}>{text}</TabWrapper>
}
Solution 6:[6]
Consider styled components documentation gives example of using reacts context api [2] for different themes.
Solution 7:[7]
Exporting styled-component
Button
and passing scrollPosition as a prop in functional component
PassingPropsToSyledComponent
import styledComponents from "styled-components";
            
export const Button = styledComponents.div`
       position: ${ props => props.scrollPosition ? 'relative' : 'static' };
`;
export const PassingPropsToSyledComponent = ()=> {
   return(
   <Button scrollPosition={scrollPosition}>
       Your Text Here
   </Button>
   )
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source | 
|---|---|
| Solution 1 | dropbeardan | 
| Solution 2 | Jöcker | 
| Solution 3 | Roy Shilkrot | 
| Solution 4 | galo hernandez | 
| Solution 5 | |
| Solution 6 | Cisum Inas | 
| Solution 7 | Muhammad Usman | 
