I am using SCSS modules for my components in React/Next.js but I can't figure out how to import kebab-case classes.
At the moment, I am just writing all my SCSS classes in camelCase but this isn't ideal as this means I cannot make use of SCSS cascading.
(I'm still learning React am I'm not so great at making dynamic components myself so I am sticking to React Strap for now.)
Essentially, I want to write
.company-logo
instead of:
.companyLogo
EDIT: className={styles['company-logo']} causes an unexpected token error
Here is my Component:
import styles from './styles/Navbar.module.scss'
const NavComponent = (props) => {
const [isOpen, setIsOpen] = useState(false);
const toggle = () => setIsOpen(!isOpen);
return (
<div>
<img src="../ss-logo.png" alt="Logo" className={styles.companyLogo}/>
</div>
);
}
export default NavComponent;
And my SCSS:
.companyLogo {
height: 3rem;
}
className={styles['company-logo']}
should be what you want.
You can use the object key [] syntax.
<img src="..." className={styles['company-logo']}`
<img src="../ss-logo.png" alt="Logo" className={`${style['company-logo']}`}/>
inline:
<img src="../ss-logo.png" alt="Logo" className={`${styles["company-logo"]}`}/>
variable:
var logo = classNames({
[`${styles["company-logo"]}`]: true,
});
return (
<div>
<img src="../ss-logo.png" alt="Logo" className={logo}/>
</div>
)
Related
I'm working on a react with nextjs project.
I'm using Link to scroll to a specific section on the same page.
Here is one of the components that use Link:
import styles from './section1.module.scss';
import Image from 'next/image';
import Button from '#material-ui/core/Button';
import tought_process from '../../../public/thought_process.png';
import Link from 'next/link';
const Section1 = () => {
return (
<div className={styles.container}>
<div className={styles.left}>
<div className={styles.leftContainer}>
<Link href='#enews'>
<div className={styles.buttonContainer}>
<Button className={styles.buttonstyle1}>Get started</Button>
</div>
</Link>
</div>
</div>
<div className={styles.right}>
<Image
src={tought_process}
className={styles.imageStyle}
alt='how to think about organizing'
layout='responsive'
priority
/>
</div>
</div>
);
};
export default Section1;
And here i mark the element with the id:
<div {...handlers} className={styles.bigBody}>
<NavBar open={menuOpen} toggle={setMenuOpen} scrollY={scrollY} />
<SideMenu open={menuOpen} toggle={setMenuOpen} scrollY={scrollY} />
<div className={styles.sections}>
<Section1 />
<Section2 />
<Section3 id='enews' />
<Section4 />
</div>
Can't figure out what i'm doing wrong.
Multiple clickable elements are wrapping each other. Remove the button and add the anchor element.
<Link href="#enews">
<a>Get started</a>
</Link>
<Link href="#enews">
<a className={styles.buttonContainer}>
<span className={styles.buttonstyle1}>Get started</span>
</a>
</Link>
I'd recommend updating the styles so you can remove the inner span element.
I use a custom link component that does a few things (not shown); one is smooth scroll to hash routes if the browser supports smooth scrolling (not Safari).
import NextLink, { LinkProps } from "next/link";
import { HTMLProps, MouseEvent, FC } from "react";
export const Link: FC<LinkProps & HTMLProps<HTMLAnchorElement>> = ({ as, children, href, replace, scroll, shallow, passHref, ...rest}) => {
const onClick = (event: MouseEvent<HTMLAnchorElement>) => {
if (href.startsWith("#")) {
event.preventDefault();
const destination = document.getElementById(href.substring(1));
if (destination) destination.scrollIntoView({ behavior: "smooth" });
}
};
return (
<NextLink as={as} href={href} passHref={passHref} replace={replace} scroll={scroll} shallow={shallow}>
<a href={href} {...rest} onClick={onClick}>
{children}
</a>
</NextLink>
);
};
I removed new lines to condense the code block
If you went with the above approach, don't include the anchor tag since it's automatically included.
import { Link } from "./custom/path/link"
<Link href="#enews">Get started</Link>
Two points here:
As per the nextjs, passHref has to be used if a custom element is used as a child of Link tag instead of an anchor tag.
As per the same docs value of href should be '/#enews' not '#enews'
This is a simplified React component that uses helmet to update the link css on runtime:
function App() {
const [brand, setBrand] = useState('nike')
return (
<div className="App">
<Helmet>
<link rel="stylesheet" href={getBrandStyle(brand)} />
</Helmet>
<div>other contents here</div>
<!-- omitted the button components that change the brand state by calling setBrand -->
</div>
);
}
I have recently just used react-helmet as a declarative way to change the head tag's child and with the code I wrote above, when switching the css there is momentary lag when the page has no css stylings and then 1 second later the updated css shows up.
Even during the initial load of the page, if I use queryParameters (code above doesn't show the query parameter approach) such as
https://localhost:3000?brandQueryParam=nike
there is 1 second wherein there is no css styling before the brand css shows up.
Can you please let me know what I am missing and how to resolve this?
This is the solution that I came up with, not sure if setTimeout is the best solution so if anyone else knows a better way, please share it.
const brands = {
nike: 'nike2022',
adidas: 'adidas2017',
fila: 'fila2020'
};
function App() {
const [brand, setBrand] = useState('nike')
const [isLoading, setIsLoading] = useState(false)
const changeBrandStyleOnClick = (brand) => {
setBrand(brand)
setIsLoading(true)
}
return (
<div className="App">
<Helmet>
<link rel="stylesheet"
onChangeClientState={(newState, addedTags, removedTags) => setTimeout(() => setIsLoading(false), 1500)}
href={getBrandStyle(brand)} />
</Helmet>
{isLoading && (
<Overlay>
<Spinner/>
</Overlay>
)}
{!isLoading && (
<>
{Object.keys(brands).filter(b => b !== brand).map(b =>
(<Button onClick={() => changeBrandStyleOnClick (b)} value={b}>
<Logo
alt="default alt name"
appearance="default"
name={b}
size="small"/>
</Button>))
}
<div>other contents here</div>
</>
)}
</div>
);
}
I am building a website using reactjs. I have a js file which extract props from another file which is an array called sections with data like title and imageurl. I need to use the prop of imageuURL as a background for each element. I tried to use style but it doesn't work.
Here is the code of extracting :
import React from 'react'
const Menuitem = (props) => {
return(
<div>
<h1 className='title'>{props.title}</h1>
<span className='subtitle'>shop now</span>
</div>
)
}
I pass that code through array in app.js using the following function:
function extract(item) {
return <Menuitem title={item.title}/>
}
Then use map function to return result
function App() {
return (
<div className=''>
{sections.map(extract) }
</div>
);
}
The result I get is like the image. I need to get a background image for each section from the array file
( imageURl prop )
react js problem
If the imageURL is inside the props passed to the menu item you can just put it in inline
const Menuitem = (props) => {
return(
<div style={{ backgroundImage: `url(${props.imageUrl})` }}>
<h1 className='title'>{props.title}</h1>
<span className='subtitle'>shop now</span>
</div>
)
}
I have a Next.js app with two "layout" components:
/layouts/default-layout.tsx:
import { Footer } from '../components/footer';
import { Header } from '../components/header';
import './default-layout.scss';
export const DefaultLayout: React.FC = ({ children }) => (
<div className="d-flex flex-column vh-100">
<Header className="flex-shrink-0 fixed-top" />
<main className="flex-shrink-0">
{children}
</main>
<Footer className="mt-auto" />
</div>
);
/layouts/profile-layout.tsx:
import Helmet from 'react-helmet';
import 'bootstrap/scss/bootstrap.scss';
import './profile-layout.scss';
export const ProfileLayout: React.FC = ({ children }) => (
<div>
<Helmet
link={[
{ rel: 'stylesheet', href: 'https://fonts.googleapis.com/css?family=Poppins|Open+Sans|Dancing+Script&display=swap' },
]}
/>
{children}
</div>
);
Both layout components import some global SCSS styles.
Pages use one layout or the other like this:
const IndexPage: NextPage = () => (
<DefaultLayout>
{/* more content here */}
</DefaultLayout>
);
Most pages use the default layout, but my dynamic pages at /pages/profiles/[id].tsx use the profile layout. Navigating between pages works fine and each page uses the correct layout and correct SCSS files.
But if I type a page directly into the web browser's address bar, I get inconsistent results. Often the wrong SCSS file is used. Having the app open in another tab and navigating around seems to effect it too.
How can I have the correct SCSS styles loaded consistently?
I am trying to create a pattern so that all the subscriptions are ready before I load the main page. Similar to Iron Router waitOn.
Take a look at this react component:
export const PageContainer = React.createClass({
render() {
return (
<div id="content-box">
<div className="banner banner-primary">
<div className="page_title pull-left">
{this.props.pageName}
</div>
</div>
<div>
{ FlowRouter.subsReady() ? this.props.page : (
<div> Loading .... </div>
)
}
</div>
</div>
);
}
});
as you can see I am using the FlowRouter.subsReady() helper to render the page or the loading text.
The problem is that this is not reactive. It just renders once but does not update and show the page once the subscription is ready.
How can I get this to be reactive?
What is the best way to use Flow Router's subscription management with React. I have a base layout and want to show loading sign before loading the page main. If I could get this function to be reactive it should work just fine.
UPDATE:
It seems like I have to attach the helper, FlowRouter.subsReady() to the get Meteor data function
export const PageContainer = React.createClass({
mixins: [ ReactMeteorData ],
getMeteorData() {
return {
isLoading: FlowRouter.subsReady()
}
},
render() {
return (
<div id="content-box">
<div className="banner banner-primary">
<div className="page_title pull-left">
{this.props.pageName}
</div>
<i className="fa fa-question-circle help-icon pull-right"></i>
</div>
<div>
{ this.data.isLoading ? this.props.page : (
<div> Loading ... </div>
)
}
</div>
</div>
);
}
});
It seems to be working now. Is this the way to do it?
You accessed the problem in the wrong direction. You don't really need to check the subsReady of FlowRouter when using meteor with react. Just install the mixin ReactMeteorData and set the this.data properly, it will reactively render the Dom. More details here
React render is not reactive. The Dom is only re-rendered when the props or state of the component is changed