NextJS How to add component level css based on conditions - javascript

In my nextjs-app, I have a component where I want to add conditional styling.
So I have made a component-level stylesheet MyComponent.module.css, which has:
.primary {
color: blue;
}
.secondary {
color: orange
}
Then, in my component, I tried to do this:
import styles from "./MyComponent.module.css"
export default function MyComponent({ text, type }) {
return (
<button className={`${type === "primary" ? styles.primary : styles.secondary}`}>
{text}
</button>
)
}
but this doesn't work e.g. returns undefined
So, how can I solve this?

Try removing the backticks.
import styles from "./style.module.css";
const App = () => {
const type = "primary";
const text = "Hello"
return (
<div className={type === "primary" ? styles.primary : styles.secondary}>
{text}
</div>
);
};
export default App;

Related

Passing props to makeStyles react

I have this component
const MyComponent = (props) => {
const classes = useStyles(props);
return (
<div
className={classes.divBackground}
backgroundImageLink={props.product?.image}
sx={{ position: "relative" }}
></div>
);
};
export default MyComponent;
I am trying to pass backgroundImage link in props and trying to put into makeStyles
export default makeStyles(props => ({
divBackground:{
background:`url("${props.backgroundImageLink}")`,
}
}));
But this does not works
& I am getting this warning in console
index.js:1 Warning: React does not recognize the `backgroundImage` prop on a DOM element. If you intentionally want it to appear in the DOM as a custom attribute, spell it as lowercase `backgroundimage` instead. If you accidentally passed it from a parent component, remove it from the DOM element.
You're not supposed to pass arbitrary attributes to the native elements (div in this case) because it doesn't do anything. The prop only works when passed in useStyles:
export default makeStyles({
divBackground: {
background: props => `url("${props.product?.image}")`,
}
});
Usage
const MyComponent = (props) => {
// you only need to pass the props here. useStyles will then use the
// prop.product.image to create the background property, generate a
// stylesheet and return the class name for you.
const classes = useStyles(props);
return (
<div
className={classes.divBackground}
// remove this line -----> backgroundImageLink={props.product?.image}
sx={{ position: "relative" }}
></div>
);
};
const MyComponent = (props) => {
const classes = useStyles(props)();
return (
<div
className={classes.divBackground}
backgroundImageLink={props.product?.image}
sx={{ position: "relative" }}
></div>
);
};
export default MyComponent;
then :
export default useStyles=(props)=>makeStyles(()=> ({
divBackground:{
background:`url("${props.backgroundImageLink}")`,
}
}));

Using CSS module class in a function, React

How do I use a CSS module class in a function?
an example: I have the className {styles.item} but I want to use that within a function such as...
export default function MenuItem {
if(props.type == "extra"){
{styles.item}.classList.add("extra");
}
return (
<MenuItem name="test product" type="extra" />
);
}
I have tried using it just as styles, with {} and with ${}
So what I want to do is if the has a type of "extra" then I want to add a dashed border.
https://github.com/JedWatson/classnames
You can install this page and use it in your react app
npm install classnames or yarn add classnames.
The Css
.success {
color: green;
}
.error {
color: red;
}
The JS
import styles from './alert.module.css'
import cn from 'classnames'
export default function Alert({ children, type }) {
return (
<div
className={cn({
[styles.success]: type === 'success',
[styles.error]: type === 'error'
})}
>
{children}
</div>
)
}
<div className={`anyClass ${props.type==='extra' ? 'extra' : null}`}/>
styles.item is a string (a class name).
It's not clear what you're trying to do with {styles.item}.classList.add( ... ).
If you want to further distinguish "extra" from "item" you should add an additional class to your stylesheet and pass them both:
<MenuItem className={`${styles.item} ${styles.extra}`} />
There are libraries like clsx to help with conditionally assembling class names:
const classes = clsx(
styles.item,
{
[styles.extra]: props.type === 'extra'
}
);
<MenuItem className={classes} />
You can use it in a function just like any other string or variable:
function Foo () {
// nothing special about styles.item. it's just a string.
const classes = props.type === 'extra'
? `${styles.item} ${styles.extra}`
: styles.item;
return classes;
}
Say you had the following function:
const getStyleClass = () =>
if (condition){
return "one"
} else {
return "two
}
You could use the function like so:
<MenuItem name="test product" type="extra" className = {() => {getStyleClass()}}/>
Keep in mind however, that the function will be called once and updating the css class dynamically will require a bit more work

How to use styled-components in react component

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;
`;

ReactJS: How to change state property value in a component, when the state property value in another component is a certain value

I am working on a React application and I am using Redux to store the state. I have the following code.
menu.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import MenuCategory from '../../components/menu-category/menu-category.component'
import NewCategoryButton from '../../components/new-category-button/new-category-button.component';
import EditMenuButton from '../../components/edit-menu-button/edit-menu-button.component';
import './menu.styles.scss';
class MenuPage extends Component {
state = {
menuEditable: false
}
render() {
return (
<div className='menu-page'>
{this.props.menu ? this.props.menu.map(category => <MenuCategory key={category._id} {...category} />) : null}
<div className='edit-menu-buttons'>
<div className='menu-button'>
{this.props.currentUser ? <NewCategoryButton /> : null}
</div>
<div className='menu-button'>
{this.props.currentUser ? <EditMenuButton /> : null}
</div>
</div>
</div>
)
}
}
const mapStateToProps = state => ({
currentUser: state.user.currentUser,
menu: state.menu
})
export default connect(mapStateToProps)(MenuPage);
edit-menu-button.component.jsx:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import Button from '../button/button.component';
class EditMenuButton extends Component {
state = {
text: "Edit Menu"
}
changeText = () => {
const { text } = this.state;
if(text === "Edit Menu") {
this.setState({text: "Save Edits"});
} else {
this.setState({text: "Edit Menu"});
}
}
render() {
const { text } = this.state;
return (
<Button onClick={ () => { this.changeText()} } style={{ backgroundColor: text === "Save Edits" ? "#b9edee" : "#222222", color: text === "Save Edits" ? "#000000" : "#ffffff", border: text === "Save Edits" ? "none" : "1px solid #222222"}}>{text}</Button>
);
}
}
export default (EditMenuButton);
I have a EditMenuButton component that has a state with a text property, and a MenuPage component that has a state with a menuEditable property.
When the text property in EditMenuButton component has a value of 'Save Edits', I want the menuEditable property in the MenuPage component to have a value of true.
I have data that is external to the components in my application that make up the Redux store (e.g. using the createStore() function). However, I am not sure how to make the state property value change when the state is inside a component, as in the case of the MenuPage and EditMenuButton components. Any insights are appreciated.
Usually its best practice in React to store state in parents, and flow that down into the children through props, focusing on having one single source of truth for the state of your application.
Consider only having menuEditable in your MenuPage, and passing that into MenuButton through a prop:
class MenuPage extends Component {
state = {
menuEditable: false
}
toggleMenuEditable = () => {
this.setState((state) => ({
menuEditable: !state.menuEditable;
}));
}
render() {
return (
<div className='menu-page'>
{this.props.menu ? this.props.menu.map(category => <MenuCategory key={category._id} {...category} />) : null}
<div className='edit-menu-buttons'>
<div className='menu-button'>
{this.props.currentUser ? <NewCategoryButton /> : null}
</div>
<div className='menu-button'>
{this.props.currentUser ? <EditMenuButton onClick={this.toggleMenuEditable} isEditing={this.state.menuEditable} /> : null}
</div>
</div>
</div>
)
}
}
Your EditMenuButton can now be significantly simpler. You'll need to make two changes: remove the state and instead use the isEditing prop, and pass the onClick prop to the HTML button
class EditMenuButton extends Component {
render() {
const { isEditing, onClick } = this.props;
return (
<Button onClick={onClick} style={{ backgroundColor: isEditing ? "#b9edee" : "#222222", color: isEditing ? "#000000" : "#ffffff", border: isEditing ? "none" : "1px solid #222222"}}>{isEditing ? "Save Edits" : "Edit Menu" }</Button>
);
}
}
Your EditMenuButton now does not have to be concerned about managing state, it just reacts to the props coming into it, which will make it easier to work with (less to think about) and less prone to bugs. You could even simplfy this further by making it a function component.
Create a function to update state in your parent component and pass it to the child component. In your parent component declare:
toggleMenuEditable = () => {
this.setState({ menuEditable: !this.state.menuEditable });
}
Pass it to the child component as a prop and call it in the changeText function.
Something like this:
changeText = () => {
const { text } = this.state;
if(text === "Edit Menu") {
this.setState({text: "Save Edits"});
this.props.toggleMenuEditable();
} else {
this.setState({text: "Edit Menu"});
}
}

Objects are not valid as a React child with Styled components

I am trying to render an image. Here's my code:
Accordion.jsx
import React from 'react';
import ArrowTemplate from "./ArrowTemplate";
function Accordion() {
return (
<div>
<ArrowTemplate arrowType={"BlackDown"} styles={""}/>
{/*<ArrowTemplate arrowType={"BlackUp"}/>*/}
{/*<ArrowTemplate arrowType={"WhiteDown"} styles={"background:black"}/>*/}
{/*<ArrowTemplate arrowType={"WhiteUp"} styles={"background:black"}/>*/}
</div>
);
}
export default Accordion;
ArrowTemplate.jsx
import BlackDownArrowSVG from './svgs/black-down-arrow.svg';
import WhiteDownArrowSVG from './svgs/white-down-arrow.svg';
import styled from 'styled-components';
import PropTypes from 'prop-types';
ArrowTemplate.propTypes = {
arrowType: PropTypes.string,
styles: PropTypes.string,
};
function ArrowTemplate(props) {
const {arrowType, styles} = props;
switch (arrowType) {
case "WhiteDown":
return (
styled.img.attrs({
src: WhiteDownArrowSVG,
})`${styles !== null ? styles : ""}`
);
case "BlackDown":
return (
styled.img.attrs({
src: BlackDownArrowSVG,
})`${styles !== null ? styles : ""}`
);
case "WhiteUp":
return (
styled.img.attrs({
src: WhiteDownArrowSVG,
})`transform: rotate(180deg); ${styles !== null ? styles : ""}`
);
case "BlackUp":
return (
styled.img.attrs({
src: BlackDownArrowSVG,
})`transform: rotate(180deg); ${styles !== null ? styles : ""}`
);
default:
throw("You need to pass arrowType");
}
}
export default ArrowTemplate;
The SVG paths are correct.
As the error I get this:
Objects are not valid as a React child (found: object with keys {$$typeof, render, displayName, attrs, componentStyle, foldedComponentIds, styledComponentId, target, withComponent, warnTooManyClasses, toString}). If you meant to render a collection of children, use an array instead.
in ArrowTemplate (at Accordion.jsx:7)
When I console.log I get a long object. But I get a similar object when I console.log the example code from the docs:
const Title = styled.h1`
font-size: 1.5em;
text-align: center;
color: palevioletred;
`;
//^^^^^^^^^^^^^^^^^^^^^
//This renders with no problem
Where am I going wrong here?
If you come across this when declaring a styled component, you could be forgetting the template literals at the end of the declaration. So
const AppContentWithSideBar = styled((props) => {
return (
<Wrapper>
<Router>
<SideBarWrapper>
<SideBar />
</SideBarWrapper>
<MainWrapper>
{props.children}
</MainWrapper>
</Router>
</Wrapper>
);
});
should be
const AppContentWithSideBar = styled((props) => {
return (
<Wrapper>
<Router>
<SideBarWrapper>
<SideBar />
</SideBarWrapper>
<MainWrapper>
{props.children}
</MainWrapper>
</Router>
</Wrapper>
);
})``;
Try:
import BlackDownArrowSVG from './svgs/black-down-arrow.svg';
import WhiteDownArrowSVG from './svgs/white-down-arrow.svg';
import styled from 'styled-components';
import PropTypes from 'prop-types';
ArrowTemplate.propTypes = {
arrowType: PropTypes.string,
styles: PropTypes.string,
};
function ArrowTemplate(props) {
const {arrowType, styles} = props;
let StyledArrowTemplate;
switch (arrowType) {
case "WhiteDown":
StyledArrowTemplate = (
styled.img.attrs({
src: WhiteDownArrowSVG,
})`${styles !== null ? styles : ""}`
);
break;
case "BlackDown":
StyledArrowTemplate = (
styled.img.attrs({
src: BlackDownArrowSVG,
})`${styles !== null ? styles : ""}`
);
break;
case "WhiteUp":
StyledArrowTemplate = (
styled.img.attrs({
src: WhiteDownArrowSVG,
})`transform: rotate(180deg); ${styles !== null ? styles : ""}`
);
break;
case "BlackUp":
StyledArrowTemplate = (
styled.img.attrs({
src: BlackDownArrowSVG,
})`transform: rotate(180deg); ${styles !== null ? styles : ""}`
);
break;
default:
throw("You need to pass arrowType");
}
return <StyledArrowTemplate />;
}
export default ArrowTemplate;
EDIT:
Apologies for the lack of initial lack of explanation!
So the reason this error occurs is when you return some sort of Object in your JSX code. In this case it is styled.img.attrs. So to get around this, we declare a variable which will then be set to be the styled component inside one of the cases, depending on what props you provide to the ArrowTemplate component, and return it at the end of the function.
This way, you are basically creating a StyledComponent as you normally would, but in a dynamic way.
Lindsay's above answer probably works fine, but I think it would make even more sense not to have a switch case, but to return a single component and pass in your conditions as props, doing your conditional logic in the definition of the styled component, i.e. something like...
const ArrowTemplate = styled.div`
src: ${props => props.color === Black ? BlackDownArrowSVG : WhiteDownArrowSVG};
${props => props.direction === Down ? transform: rotate(180) : null }
etc...
`;
(not sure I have the above syntax exactly right, but this is the basic idea)
and
<ArrowTemplate color={Black} direction={Up} src={src} />
const Button = styled.button`
font: inherit;
padding: 0.5rem 1.5rem;
border: 1px solid #8b005d;
color: white;
background: #8b005d;
box-shadow: 0 0 4px rgba(0, 0, 0, 0.26);
cursor: pointer;
`
You are getting this error because you are storing the value in the function instead of the variable. let me give you an example and let you know.
This will work for you.

Categories

Resources