I am relatively new to React (after coming over from Angular) and am having trouble with trying to access a property of a styled component when passed into it.
I get this error:
/src/chat/Container.js
Line 115:5: 'cssOverrides' is not defined no-undef
Search for the keywords to learn more about each error.
Here is my App.js component:
const [isOpen, setIsOpen] = React.useState(false);
const cssOverrides = useSelector((state) => state.cssOverrides.ChatWindow)
return (
<Provider store={store}>
{isOpen ? (
<ChatContainer setIsOpen={setIsOpen} cssOverrides={cssOverrides} />
) : (
<LauncherContainer setIsOpen={setIsOpen} cssOverrides={cssOverrides} />
)}
</Provider>
);
And one of the child components:
export default function Container({ setIsOpen, cssOverrides }) {
const [isClosing, setIsClosing] = React.useState(false);
And the styled component (where I am trying to usee the css string passed into it as a property)
const Launcher = styled.div`
box-shadow: rgba(0, 0, 0, 0.16) 0px 5px 40px;
border-radius: 6px 6px 0 0;
position: absolute;
bottom: 0;
height: 38px;
width: 375px;
right: 20px;
position: fixed;
overflow: hidden;
${cssOverrides}
animation: ${({ isClosing }) =>
isClosing
? css`
${slideDown} ${CLOSING_DURATION}ms
`
: css`
${slideUp} 200ms
`};
`;
Can anyone offer any advice as to why this error is happening? Thanks
You need to use the property as a callback.
// Good
const Launcher = styled.div`
${({ cssOverrides }) => cssOverrides}
`;
// Bad
// Looks for cssOverrides value in the outer scope, where its not defined.
const Launcher = styled.div`
${cssOverrides}
`;
// Usually such syntax used for css blocks
import styled, { css } from 'styled-components'
// Not passed as prop, located in the outer scope
const cssOverrides = css`
background-color: blue;
`
// Good
const Launcher = styled.div`
${cssOverrides}
`;
I found the answer. I forgot to add the property cssOverrides to the Launcher component instance in the JSX
<Launcher isClosing={isClosing} cssOverrides={cssOverrides}>
Related
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
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.
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
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{}
`
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;
`;