How to use styled-components in react component - javascript

Total newbie on using styled-components. I'm wondering what's the usage of it? How should I implement component life cycle methods after styling it? For simplicity sake I've removed all the other style.
import styled from 'styled-components';
const Button = styled.button`
background-color: 'green'
`
export default Button;
I'm wondering how do I further working on this Button component?
Traditionally we can declare a class-based component and implement some lifecycle methods, but now with this styled-components, I'm not really sure how to combine them together as they are really the single Button Component?
UPDATES:
Full sourcecode for Button.js. By having the below code, all styles will be gone and I can't understand the problem
import React from 'react';
import styled from 'styled-components';
// import Button from 'react-bootstrap/Button';
import color from '../config/color';
const Button = ({ children, onPress }) => (
<button type="button" onPress={onPress}>{children}</button>
);
const StyledButton = styled(Button)`
width: 12rem;
height: 54px;
font-size: 1rem;
background-color: ${(props) => {
if (props.inverted) return 'white';
if (props.disabled) return color.disabled;
return (props.color || color.primary);
}};
color: ${(props) => {
if (props.disabled) return color.disabledText;
if (props.inverted) return (props.color || color.primary);
return 'white';
}};
border:${(props) => (props.inverted ? `2px solid ${props.color || color.primary}` : 'none')};
border-radius: 60px;
&:hover {
filter: ${(props) => (props.inverted || props.disabled ? 'none' : 'brightness(95%)')}
}
`;
export default StyledButton;

In order to style a custom react component you can pass on the custom component name as argument to styled. According to the doc:
The styled method works perfectly on all of your own or any
third-party component, as long as they attach the passed className
prop to a DOM element.
import React from 'react';
import styled from 'styled-components';
// import Button from 'react-bootstrap/Button';
import color from '../config/color';
const Button = ({ children, className onPress }) => (
<button type="button" className={className} onPress={onPress}>{children}</button>
);
const StyledButton = styled(Button)`
width: 12rem;
height: 54px;
font-size: 1rem;
background-color: ${(props) => {
if (props.inverted) return 'white';
if (props.disabled) return color.disabled;
return (props.color || color.primary);
}};
color: ${(props) => {
if (props.disabled) return color.disabledText;
if (props.inverted) return (props.color || color.primary);
return 'white';
}};
border:${(props) => (props.inverted ? `2px solid ${props.color || color.primary}` : 'none')};
border-radius: 60px;
&:hover {
filter: ${(props) => (props.inverted || props.disabled ? 'none' : 'brightness(95%)')}
}
`;
export default StyledButton;
Read the styled-component documentation for more details on styling any component

Let's rename the styled button component to reduce confusion between the 2 similarly named components.
styled-button.tsx:
import styled from 'styled-components';
const StyledButton = styled.button`
background-color: 'green'
`
export default StyledButton;
When you import the styled button component into your Button component, you can actually use make use of it the way you usually do when you are working with traditional HTML <button> elements, as its props are exposed and available on the styled component as well.
button.tsx:
import StyledButton from './StyledButton'
class Button extends React.Component {
componentDidMount() {
const { someProps, otherProps } = this.props;
// some lifecycle logic
}
handleClick() {
// do the rest
}
render() {
return <StyledButton onClick={() = this.handleClick()} />;
}
}
If you want, you can even pass in props from the parent Button component, to the child StyledButton component. This will allow you to customise it.
render() {
const { color } = this.props;
return <StyledButton background={color} onClick={() = this.handleClick()} />;
}
And on your StyledButton component, you just need to make the following changes:
const StyledButton = styled.button`
background-color: ${({ color }) => color || 'green'}
`

What other answers lack is for styling custom components like Button you have to pass a className prop thought it.
The styling is injected through className property.
const ButtonDefaultStyle = styled.button`
width: 5rem;
`;
const Button = ({ className, children, onPress }) => (
<ButtonDefaultStyle className={className} type="button" onPress={onPress}>
{children}
</ButtonDefaultStyle>
);
export default Button;
Then the styles can be applied:
import Button from './Button.js'
// Will override width: 5rem;
const StyledButton = styled(Button)`
width: 12rem;
`;

Related

How to shorten passed items in components

I use styled-components each component from styled-components I pass in other components, in order to apply them, the problem is that my code looks ugly because every component style I pass in other components it looks like this
SideBarStyledComponents.js
export default function SideBarStyledComponents(props) {
const {SideBarValue} = React.useContext(CounterContext);
const [SideBarThemeValue] = SideBarValue;
const PageColor = SideBarThemeValue && SideBarThemeValue.PageContentColor;
const AlertBg = SideBarThemeValue && SideBarThemeValue.AlertBackground;
const LessonContainers = styled.div`
margin: 2rem 0 2rem 0;
`;
const LessonSideBarTitle = styled.h1`
font-size: 1.8rem;
font-weight: 500;
color: ${(PageColor ? PageColor : "#2c3e50")};
font-family: 'Roboto';
margin-top: 1rem;
`;
return(
<RoutesPage {...props} LessonContainers={LessonContainers} SideBarThemeValue={SideBarThemeValue}
LessonSideBarTitle={LessonSideBarTitle}/>
);
}
RoutesPage.js
function RoutesPage(props) {
const {path} = props.path;
const routes = [
{
path: `${path}/Introduction`,
component: () => <Introduction {...props} />
},
{
path: `${path}/Creating Your First Javascript`,
exact: true,
component: () => <CreatingFirstJS {...props} />
},
{
path: `${path}/Guardian`,
component: () => <h2>Shoelaces</h2>
}
];
return (
<>
<Switch>
{routes.map((route, index) => (
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.component}
/>
))}
</Switch>
</>
);
}
Please pay attention, you have noticed every style I pass to the components and so every time I create a new component every style I have to pass this way I will have many components since I am creating a sidebar I want to know if there is a way to get rid of this
You should define all the styled components outside in a separate file (or multiple files). And then you should import those styled components directly within your component where you are going to use it.
Passing them as props is a bad practice.
For example you can create a file called 'StyledComponents.js' and export all your styled components.
...
export const LessonContainers = styled.div`
margin: 2rem 0 2rem 0;
`;
export const LessonSideBarTitle = ({children}) => {
const {SideBarValue} = React.useContext(CounterContext);
const [SideBarThemeValue] = SideBarValue;
const PageColor = SideBarThemeValue && SideBarThemeValue.PageContentColor;
const H1 = styled.h1`
font-size: 1.8rem;
font-weight: 500;
color: ${(PageColor ? PageColor : "#2c3e50")};
font-family: 'Roboto';
margin-top: 1rem;
`;
return <H1>{children}</H1>
}
...
And now in the Introduction or CreatingFirstJS component, you can just import the necessary styled components like so:
import {LessonSideBarTitle} from 'path/to/StyledComponents';
Another way is to take advantage of the styled object properties, to remove the ugly long interpolation of props by destructuring the props
import styled from "styled-components"
const Button = styled.button(
({ bgColor, fontColor }) => `
background: ${bgColor};
color: ${fontColor};
`
);
function App() {
return (
<Button bgColor="#000" fontColor="#fff"> Hello World </Button>
)
}
Example in codesandbox

React styled-components - How to render styles based on prop condition or pseudo class?

I am trying to conditionally render a hover state / view within styled-components, leveraging the props coming in from react...
Currently, my code looks something like this:
${isHovered}, &:hover {
background: red;
}
Unfortunately, this does no work to be able to have a true/false as I am missing something I presume to be able to do the either / or pseudo..
I want to be able to explicitly show the hover state while retaining the default pseudo hover. How can I achieve this?
The way you have your selectors right now is invalid:
Problem
${isHovered}, &:hover {
background: red;
}
As of now, this will always translate to:
undefined, &:hover {
background: red;
}
For simplicity, in order to interpolate properly, it must be a function accepting props that returns a string:
color: ${props => props.isHovered && "pink" };
On a related note, even if you wrapped your styled component in a higher order component where isHovered is defined as an argument, it unfortunately still won't work in production for styled-components v5 -- this worked in v4, but v5.x doesn't handle css interpolations properly within a styled component when compiled for production (see issue tracker here).
Solutions
A better and recommended approach would be to interpolate within a CSS property:
background: ${({ isHovered }) => isHovered && "red"};
:hover {
background: red;
}
Alternatively, if you have multiple CSS rules, then you can interpolate outside of a CSS property:
${({ isHovered }) => isHovered && "background: red;color: white;"};
:hover {
background: red;
}
Now you just would pass your component an isHovered prop.
<StyledComponent isHovered={isHovered} />
Although, technically you can do this (notice that falsey values equate to true, which may be a bug or an unhandled edge case) ...
${({ isHovered }) => !isHovered && ".hovered"}, &:hover {
background: red;
}
...it is NOT recommended because of how it's being interpreted:
".hovered", &:hover {
background: red;
}
Arguably, this isn't what you'd want because .hovered isn't being used at all within the DOM, which may be confusing to other developers. Instead, it reuses the compiled hashed class name to add the CSS property within another rule (focus on the Styles tab to see the difference):
screenshot
While the recommended approach sets the CSS property to the hashed class within the same rule block:
screenshot
Working demo (this demo includes both example codes above, where Title uses the recommended approach and SubTitle doesn't):
import styled, { css } from 'styled-components';
const getHoverStyle = (props) => {
if(props?.isHovered) {
return (
css`
&:hover {
background: red;
}
`
)
}
}
export const SomeName = styled.div`
${getHoverStyle}
`;
You can use class selector to apply style and in your styled component you can use attr call to set className attribute based on property value like:
const MyComp = styled.div.attrs((props) => ({
className: props.isHovered ? "hovered" : ""
}))`
color: ${(props) => props.color};
&.hovered,
&:hover {
color: blue;
}
`;
Take a look at this react sandbox.
I have created an interactive playground. You can visit the below link. It might help you.
https://codesandbox.io/s/focused-sky-vp8d9?file=/src/App.js
Below is the code.
import React, { useState } from "react";
import "./styles.css";
import styled, { css } from "styled-components";
const HoverExample = styled.div`
background: pink;
width: 100%;
min-height: 80px;
${(props) =>
props.isHovered &&
css`
&:hover {
background: green;
}
`}
`;
export default function App() {
const [isHovered, setisHovered] = useState(false);
return (
<div className="App">
<button onClick={() => setisHovered(!isHovered)}>
{!isHovered ? "Enable" : "disabled"} Hover
</button>
<p>Hover is {isHovered ? "enabled" : "disabled"}</p>
<HoverExample isHovered={isHovered}></HoverExample>
</div>
);
}
Thanks.

React does not recognize the `backgroundColor` prop on a DOM element

I've been searching about this error a while but couldn't find a solution...
I'm using styled components and ant.design.
Button Component
import React from 'react'
import {Btn} from './style';
const ComponentButton = (props) =>{
const {title, backgroundColor,color, hoverColor, handleClick,shape} = props
return(
<Btn
shape={shape || "round"}
onClick={handleClick}
backgroundColor={backgroundColor}
color={color}
hoverColor={hoverColor}
>
{title}
</Btn>
)
}
export default ComponentButton;
styled-Component
import styled, {css} from 'styled-components';
import {primaryColor, white} from '../../../../config';
import { Button } from 'antd';
export const Btn = styled(Button)`
${(props, {color, backgroundColor, hoverColor, width} = props) =>
css`
color: ${color ? color : white};
background-color: ${backgroundColor ? backgroundColor : primaryColor} !important;
width: ${`${width}px` ? `${width}px` : '-webkit-fill-available'};
border: none !important;
&:hover, &:focus{
color: ${hoverColor ? hoverColor : white};
border: none !important;
}
&:after{
box-shadow: none !important;
}
`}
`
I don't know why I still getting this error:
React does not recognize the backgroundColor prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase backgroundcolor instead.
styled-components will automatically add all props into DOM element by default, such as:
<button backgroundColor="" color="" hoverColor="" ... />
and react will check the props of the DOM element are legal.
also, this line ${(props, {color, backgroundColor, hoverColor, width} = props) looks a little weird, this should only have one parameter.
you can try this:
// avoid pass all props into button element
export const Btn = styled(({color, backgroundColor, hoverColor, width, ...props}) => <Button {...props} />)`
${(p = props) =>
css`
color: ${p.color ? p.color : white};
background-color: ${p.backgroundColor ? p.backgroundColor : primaryColor} !important;
width: ${`${p.width}px` ? `${p.width}px` : '-webkit-fill-available'};
border: none !important;
&:hover, &:focus{
color: ${p.hoverColor ? p.hoverColor : white};
border: none !important;
}
&:after{
box-shadow: none !important;
}
`}
`
In case you arrive here because of the error message in the title: Here's a solution for the problem in the world of react and javascript:
/**
* This layer of *styled* will make the error go away
**/
const StylableButton = styled.button({}, (props) => ({
...props,
// Just in case you'd use typescript:
//...(props as any),
}));
/**
* Your custom button component
**/
const MyButton= (props) => {
return <StylableButton {...props} />;
};
Using it like this:
const MyStyledButton = styled(MyButton)({
backgroundColor: 'red',
})
Code for your case

Styled components nested rules don't work

Here is my folder structure:
type/
- Heading.js
- index.js
card/
- Card.js
- CardGroup.js
- index.js
JSX:
<CardGroup>
<Heading>...</Heading>
<Card>...</Card<
</CardHeading>
Now I am trying to style the Heading and Card components differently if they are nested inside a CardGroup. CardGroup.js:
import Heading from '../type';
import Card from './Card';
const CardGroup = styled.div`
${Heading} { ... }
${Card} { ... }
`;
Works OK for the Heading but NOT for the Card. I came accross this issue before and I can't wrap my head around what's causing this. Is it because the are in the same folder? Is it because of the order they are imported in my app? Any ideas would be really helpful.
Updated:
My Card.js implementation:
const StyledCard = styled.div`...`;
const Card = props => {
...
<StyledCard {...props}>{props.children}</StyledCard>
}
You need to target the component generated by styled-component (StyledCard in your example).
// Card.js
const ContainerCard = styled.div`
width: 50px;
height: 50px;
`;
const Card = ({ className }) => {
return <ContainerCard className={className} />;
};
// Use any valid prop, Card.StyledComponent, Card.Style etc.
Card.className = ContainerCard;
export default Card;
// App.js
const Container = styled.div`
height: 100vh;
width: 100vw;
`;
// Styling custom components, through className prop.
const VioletredRedCard = styled(Card)`
background-color: palevioletred;
`;
// Target the component generated by styled-components
const CardWrapper = styled.div`
${Card.className} {
width: 100px;
height: 100px;
background-color: paleturquoise;
}
`;
const App = () => {
return (
<Container>
<CardWrapper>
<Card />
</CardWrapper>
<VioletredRedCard />
</Container>
);
};
And, of course, if you want to style the card via: styled(Card), be sure you pass the className prop like in the example above.
You can use classname attr with styled components.
const Card = styled.div.attrs({
className:"card"
})`
card styles
`
and you can use that className in CardGroup
const CardGroup=styled.div`
& .card{}
`

How to use Styled Components for a component passed as props

I have 3 components in 3 differents files :
//file1
const Icon1 = styled.div`
width: 10px;
`;
const Widget1 = () => <BaseWidget icon={<Icon1 />} />
//file2
const Icon2 = styled.div`
width: 15px;
`;
const Widget2 = () => <BaseWidget icon={<Icon2 />} />
//file3
const Icon3 = styled.div`
width: 20px;
`;
const Widget3 = () => <BaseWidget icon={<Icon3 />} />
and my base widget :
//basewidget
const BaseWidget = (props) => <div>{props.icon}</div>
My question is : how can I add style to props.icon in basewidget ?
Can I create a styled component based on props.icon and add a common css property ?
If it's not possible what is the best solution ?
Thanks,
Jef
When you are passing in icon={<Icon2 />} you are actually passing a JSX element, which cannot be styled from the other side with styled-components because it is not a component. To style a component, it needs to take the className as a prop. In this case, your best bet is to write a styled-component wrapper dev and let the styles cascade to your {props.icon}
But because you are not passing in any props to Icon you could easily pass in the component as a prop making it stylable.
<BaseWidget icon={Icon1} />
Where you are receiving it:
import styled from "styled-components";
const StyledIcon = styled.i``;
const BaseWidget = (props) => {
const { Icon } = props.icon;
return (
<StyledIcon as={Icon} />
);
}
as Docs

Categories

Resources