I am new to styled components and I have a wrapper component that is a div element that I want to style . But when I inspect the specific element using React Dev Tools it is named styled.div and not Wrapper.And the theme is not passed to the children of the wrapper .Why is this happening ?
My code where I return the wrapper :
const Wrapper = styled.div`
background: red;
`;
return (
<ThemeProvider theme={theme}>
<Wrapper name="wrapper" ref={target}>
{items.map((props, key) => {
return <SliderWrapper key={key} {...props} />;
})}
</Wrapper>
</ThemeProvider>
);
Related
I am trying to write a react application where the App component renders an array of components to the screen. But the inline CSS are not showing up.
//App component
import data from "./data.js"
import Item from "./Item"
export default function App(){
const cardElements = data.map(item => <Item/>)
return (<div className='app'>
{cardElements}
</div>);
}
//Item component
export default function Item(){
const customStyle = {border: "2px solid black"};
return (<div style={customStyle} >Item component</div>);
}
The inline CSS in the Item component does not reflect on the webpage.
As you can see in the snippet below the inline style does indeed work. What is likely happening here is your style is bering overridden but we'd need more information to know for sure.
Sidenote: don't forget to add key prop when using .map in React.
const data = [1, 2, 3, 4];
function App() {
const cardElements = data.map(item => <Item key={item} />)
return (
<div className='app'>
{cardElements}
</div>
);
}
function Item() {
const customStyle = { border: "2px solid black" };
return <div style={customStyle}>Item component</div>;
}
ReactDOM.createRoot(
document.getElementById("root")
).render(
<App />
);
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/18.2.0/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/18.2.0/umd/react-dom.development.js"></script>
Let's say you have a navbar and when you're using this component on your homepage you want it to have a certain background color and display property, but when you use that same navbar component on another page in your application you want to change these CSS properties. Seeing as the component has one CSS file linked how would you change the style of a component depending on where it is being rendered?
My personal favourite method nowadays is styled components. Your component might look something like this:
// NavBar.js
import styled from 'styled-components'
const StyledDiv = styled.div`
width: 100%;
height: 2rem;
background-color: ${props => props.bgColor};
`
const NavBar = (bgColor) => {
return <StyledDiv bgColor={bgColor}>
}
Then to use it in your different contexts you simply pass the color prop:
// homepage.js
<NavBar bgColor="red" />
// otherpage.js
<NavBar bgColor="#123ABC" />
Styled components are becoming a very popular way of doing things, but be aware that there are a huge array of ways you can do this.
https://styled-components.com/
(Code not tested)
Well If you just want to use plain CSS then you can change the className based on route so the styles also changes.
Example:
import { useLocation } from "react-router-dom";
const Navigation = () => {
let location = useLocation();
...
return(
<nav className={location.pathname === "/home" ? "homepage-navbar" : "default-navbar"}>
...
</nav>
)
}
You can write longer condition for multiple pages as well.
Other better thing you can do is pass the location.pathname and value of className as prop.
import { useLocation } from "react-router-dom";
const Home = () => {
let location = useLocation();
...
return (
<>...
<Navigation location={location.pathname} styleClass={"homepage-navbar"}/>
</>
)
}
const Navigation = ({location, styleClass}) => {
...
return(
<nav className={location === "/home" ? styleClass : "default-navbar"}>
...
</nav>
)
}
So now you can pass different values for className from different components and get different styles for the navbar.
I would like to implement a loading screen on first render in next.js but when I load the page the body content flashes for a moment before the loader starts. I'm not sure what should I write differently. If I change the initial state to true that doesn't seem to work either because then the wrapper styles for the loader won't apply and instead I get a blank html page with the spinner on the top left corner.
_app.js
function MyApp({ Component, pageProps }) {
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
setIsLoading(true);
setTimeout(() => {
setIsLoading(false);
}, 3000);
}, []);
return (
<>
{isLoading ? (
<Loader />
) : (
<ThemeProvider theme={dark}>
<GlobalStyles />
<Component {...pageProps} />;
</ThemeProvider>
)}
</>
);
}
export default MyApp;
Loader div style:
.loader-wrapper{
width:100%;
height: 100vh;
background: ${({ theme }) => theme.colors.body};
display: flex;
align-items: center;
justify-content: center;
}
Loader Component
export const Loader = () => {
return (
<div className='loader-wrapper'>
<Oval
ariaLabel='loading-indicator'
height={100}
width={100}
strokeWidth={2}
color='#32ffa9'
secondaryColor='#1f1f1fdc'
/>
</div>
);
};
Thats because useEffect called after component rendered.
Life cycle:
1- Initial state of isLoading = false <ThemeProvider /> rendered (body content flashes for a moment)
2- useEffect called and isLoading = true
3- Component re-rendered but this time it will render <Loader />
4- 3 second later setTimeout callback called isLoading = false
5- Component re-rendered and it will render <ThemeProvider /> again
So you should make your inital isLoading = true, I couldn't understand the issue "wrapper styles for the loader won't apply." can you describe it more?
I made codesandbox example for you which I think works as you want (it's not NextJS app but since all the code is pure React it will work on both)
The following question came up when I wanted to migrate from Styled Components to CSS Modules.
Let's say I have the following styled component which accepts a dynamic parameter offset and a dynamic CSS string theme:
const Li = styled.li`
&.selected {
background-color: grey;
}
margin-left: ${({ offset }) => offset}px;
${({ theme }) => theme};
`;
In my code, I'd use it the following way:
const Parent = () => (
<List>
{list.map((item) => (
<Item
key={item.id}
id={item.id}
selectedIds={[]}
offset={24}
theme={`
&.selected {
background-color: green;
}
`}
>
{item.name}
</Item>
))}
</List>
);
const Item = ({ id, offset = 0, theme, children }) => {
return (
<Li
theme={theme}
offset={offset}
className={selectedIds.includes(id) && 'selected'}
>
{children}
</Li>
);
};
Requirement: Now I would really keep the Item's component API: passing a number offset and a style string theme. So essentially everything in Parent component should stay this way.
How can I convert the Item component internally to use CSS Modules instead of the styled Li component?
It's probably a different way of thinking than you used to but it can work
You can use css variable
style={{ [`--offset`]: `${offset}px` }}
.item {
margin-left: var(--offset);
}
You can have a css module (file) dedicated to themes. In your case, it has withSelected
.withSelected {
&.selected {
background-color: green;
}
}
So you could pass it as "theme"
theme={themes.withSelected}
This is how the components look
import styles from "./style.module.scss";
import themes from "./themes.module.scss";
const Parent = () => (
<ul>
{list.map((item) => (
<Item
key={item.id}
id={item.id}
selectedIds={[1]}
offset={24}
theme={themes.withSelected}
>
{item.name}
</Item>
))}
</ul>
);
const Item = ({ id, offset = 0, theme, children, selectedIds }) => {
return (
<li
className={`${styles.item} ${theme} ${
selectedIds.includes(id) && themes.selected
}`}
theme={theme}
style={{ [`--offset`]: `${offset}px` }}
>
{children}
</li>
);
};
Demo: https://codesandbox.io/s/styledcomponent-to-css-modules-1kbqx
With 1 I'd concur with #Mosh to just use the style prop. Css modules are static by design and there no way to get this done otherwise (I think that styled-components also uses the style prop so you're not losing anything).
For 2 you can leverage Sass modules which allow you to define your theme in a single place and import them as required:
/theme/_colors.scss
$red: rgb(200, 0 50);
/components/Item.module.scss
#import "../theme/colors"
.selected {
background: $red;
}
Note: if you use Create React App absolute paths you can import from root as ~theme/colors
I would like to use styled-system's breakpoint and sizing mechanism inside a styled component.
Currently, I have to use 'react-media', which solves my problem, but this solution comes with a big amount of code duplication. Here is my current solution to set a StyledComponent's border-radius based on the screen size:
import React from 'react'
import styled from 'styled-components'
import theme from '../../theme'
import Media from 'react-media'
const StyledImg = styled.img`
border-radius: ${props => (props.size / 4)};
`
function ProfileImage (props) {
const breakpoint = theme.breakpoints[0]
return <Media query={`(min-width: ${breakpoint})`}>
{matches =>
matches ? (
<StyledImg size={props.size[1]} src={props.src} />
) : (
<StyledImg size={props.size[0]} src={props.src} />
)
}
</Media>
}
function ProfileCard (props) {
return <Box bg='grey' width={1}>
<ProfileImage width={[10, 20]} src={props.src}>
</Box>
}
export default ProfileImage
Is it possible to get the current breakpoint index?
Can I write something like this:
const StyledImg = styled.img`
border-radius: ${props => (props.size[theme.currentBreakpointIndex] / 4)};
`
function ProfileImage (props) {
return <StyledImg {...props} />
}
function ProfileCard (props) {
return <Box bg='grey' width={1}>
<ProfileImage size={[10, 20]} src={props.src}>
</Box>
}
export default ProfileImage
Reading this they suggest you inject breakpoints into queries and put them in the theme. You cant then access them like this:
styled.div`
${({theme: {mediaQueries: {small}}}) => small} {
color: red;
}
`
This solution uses css to change styles however (but in my opinion that's how it should be done).