Styled Components Injects wrong classes on wrong elements - reactjs

I'm witnessing a weird behavior when in styled-components with SSR in remix.run
I have a ProductCard Component that renders a normal product card with styled-components
ProductCard.tsx
import Button from "../Button";
function ProductCard({ product }: props) {
return (
<>
<Wrapper>
....
<ButtonsWrapper>
<Cart
onClick={addToCart}
mode={addedToCart ? "secondary" : "primary"}
disabled={loading}
key="cart-button"
>
{addedToCart ? "Added!" : "Add to cart"}
{loading && <LoadingSpinner src="/images/responses/loader.svg" />}
</Cart>
<ShareButton mode="secondary" aria-label="share">
<Icon id="share" />
</ShareButton>
</ButtonsWrapper>
</Wrapper>
</>
);
}
const Cart = styled(Button)`
flex: 1.1;
display: flex;
justify-content: center;
gap: 10px;
`;
const ShareButton = styled(Button)`
padding: 0.9rem;
`;
const Wrapper = styled.div`
--border-radius: ${clamp(15, 20)};
--columnGap: ${clamp(20, 30)};
display: flex;
flex-direction: column;
gap: var(--columnGap);
justify-content: space-between;
width: 100%;
height: 100%;
margin: auto;
background-color: var(--azure-15);
padding: 1.9rem 1.5rem;
border-radius: var(--border-radius);
box-shadow: var(--box-shadow-lg);
border: var(--border-lg);
`;
const ButtonsWrapper = styled.div`
display: flex;
justify-content: space-between;
gap: 0.625rem;
`;
export default ProductCard;
Button.tsx
const Button = styled.button<{ mode: "primary" | "secondary" | "dark" }>`
display: grid;
/* justify-content: center; */
align-items: center;
text-align: center;
color: var(--mintCream);
padding: ${clamp(9, 10)} ${clamp(20, 30)}; // this clamp function just generates the css clamp func with calculating the values with some equations
box-shadow: var(--box-shadow-md);
border: var(--border-md);
border-radius: 12px;
text-decoration: none;
cursor: pointer;
transition: all 500ms ease;
font-size: ${clamp(13, 16)};
&:disabled {
cursor: not-allowed;
opacity: 0.7;
}
#media (hover: hover) and (pointer: fine) {
&:hover:enabled {
transform: translateY(-2px); }
}
width: fit-content;
`;
The normal render of this Component is as follows
But when navigating to another path and returning to it on / , it renders like this
This problem only happens in production and works fine on local server...
when inspecting elements, I find that the class name of the Cart Component is also injected into the ShareButton Element
I can't find an explanation for this problem and it gets weirder... When I swap the order of the variables Cart and ShareButton or swap them with the Wrapper Element, some other weird behaviors happen like the one below
In this case, the class name of the Cart Component got injected on the parent elemnt of the parent element of the ProductCard Component
I've probably hit on 4 of these rendering issues but all of them share the same problem, the class name of the Cart Components gets injected on a wrong dom element, whether it's a parent or a sibiling
You can view the first weird behaviour here https://store.ieeenu.com
You will find the product component on the root path, navigate to some path like categories/circuits-1-ecen101 and return to the root and you will see the issue
also, you can review the second weird behavior in a previous build here
https://ieee-nu-store-r243eocii-omarkhled.vercel.app/
I just changed the initialization order of the Cart and ShareButton Components as I said earlier
I don't know whether this problem is from styled-components or from remix (this is the first time for me using remix), it's mentioned here https://github.com/remix-run/remix/issues/1032 that the lack of the babel-plugin-styled-components in remix.run introduces some problems in rehydration but I'm not sure that this is the issue I'm facing...
Thanks for reading this till the end and excuse my English, I'm not a native speaker :"

Related

React. Two buttons in a component but only one button works

I am practicing React basics.
I have a component mapped from an array of objects and it shows twice in my website but only on the second time the button works. the first button does not seem to be recognised.
Can someone explain why please? I can't find a reason for this but I am sure it is something obvious I am missing
A link to sandbox
App.js
import MainComponent from "./components/MainComponent";
function App() {
return (
<div>
<MainComponent />
</div>
);
}
export default App;
MainComponent.js
import React from "react";
import TextItem from "./TextItem";
const text = [
{
image:
"https://pbs.twimg.com/profile_images/626298192418607105/4KBKHQWi_400x400.jpg",
title: "This is a whale",
subtitle: "It is a large mammal",
price: "£ 167.87 + VAT",
uom: "per Unit",
},
{
image:
"https://news.artnet.com/app/news-upload/2019/01/Cat-Photog-Feat-256x256.jpg",
title: "This is a cat",
subtitle: "It is a small mammal",
price: "£ 17.87 + VAT",
uom: "per Unit",
},
];
const MainComponent = () => {
return (
<div>
{text.map((eachText, i) => (
<TextItem
key={i}
image={eachText.image}
title={eachText.title}
subtitle={eachText.subtitle}
price={eachText.price}
uom={eachText.uom}
/>
))}
</div>
);
};
export default MainComponent;
TextItem.js
import React from "react";
import styled from "styled-components";
import Card from "../UI/Card";
const TextItem = (props) => {
const onClickHandler = () => {
console.log("clicked");
};
return (
<Card>
<DivFlex>
<ImgDiv>
<img src={props.image} alt={"a whale"} />
</ImgDiv>
<TitleDiv>
<h1>{props.title}</h1>
<h3>{props.subtitle}</h3>
</TitleDiv>
<PriceDiv>
<h2>{props.price}</h2>
<h2>{props.uom}</h2>
</PriceDiv>
<EditDiv>
<Button onClick={onClickHandler}>Edit this</Button>
</EditDiv>
</DivFlex>
</Card>
);
};
const DivFlex = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: 800px;
height: 600px;
`;
const ImgDiv = styled.div`
width: 90px;
height: 90px;
margin-right: 3%;
img {
max-width: 100%;
height: 100%;
border-radius: 6px;
}
`;
const TitleDiv = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-start;
width: 30%;
margin-right: 3%;
`;
const PriceDiv = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end;
width: 20%;
margin-right: 3%;
`;
const EditDiv = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
width: auto;
margin-left: 3%;
`;
const Button = styled.div`
outline: none;
border: none;
background: maroon;
color: white;
padding: 1.2rem 1.8rem;
font-size: 22px;
border-radius: 8px;
cursor: pointer;
`;
export default TextItem;
This is happening due to the conflicting heights of Card and DivFlex. The Card has an height of 140px but its child DivFlex has a height of 600px. So, the next item is basically overlapping the previous item and hence you are not able to click the first button. You can observe the same behaviour if you had any number of items. Only the last item would be clickable due to it overlapping on top of previous item.
One way to fix this is to remove the height from Card component and change the height of DivFlex to 140px instead.
PS: It didn't happen in the replica sandbox because you didn't use the Card component there and used a regular div which by default has a height of auto

How to make activeClass on NavLink - styled component

Hi everyone im in trouble with active Link, i use Styled Component.
I want my link to be Red when i'm on active link but nothing work.
I tried ActiveCLassName but this not work too.
can someone help me?
thanks a lot
const NavLink = styled(Link)`
display: flex;
justify-content: center;
align-items: center;
height: 100%;
padding: 0 10px;
list-style-type: none;
text-decoration: none;
color: black;
background-color: yellow;
border: 0.1px solid lightgrey;
`;
export default function Nav() {
return (
<NavWrapper>
<UlNav>
<LiNav>
<NavLink to="/Burgers">Burgers</NavLink>
</LiNav>
<LiNav>
<NavLink to="/Burgers">Pizza</NavLink>
</LiNav>
<LiNav>
<NavLink to="/Burgers">Drinks</NavLink>
</LiNav>
</UlNav>
</NavWrapper>
)};
Issues
The issue I see is you are styling the Link component instead of the NavLink component. The Link component doesn't take any additional props for handling active links.
Solution
The NavLink component uses a .active class by default, so of you don't need any special classname you should use this class.
Example:
import { NavLink as BaseNavLink } from "react-router-dom";
const NavLink = styled(BaseNavLink)`
display: flex;
justify-content: center;
align-items: center;
height: 100%;
padding: 0 10px;
list-style-type: none;
text-decoration: none;
color: black;
background-color: yellow;
border: 0.1px solid lightgrey;
&.active {
.... your active CSS rules here
}
`;
Tested and works in both RRDv5 and RRDv6.
RRDv5
RRDv6
"/drinks"
"/burgers"
Your code is missing some stuff, like I am not seeing anywhere you are setting color: red like you want. Basically, the active link will have the class active applied, so either using a normal stylesheet or inside your styled(Link, you have to write a rule for that class that does what you want.
Like it says here Use 'active' state from React Router in Styled Components. You may have to use the &.active selector to apply the styles.
activeClassName just changes what the class name is, which isn't what you want. By default it is active which is fine, you just have to write the CSS rule to match it. https://v5.reactrouter.com/web/api/NavLink/activeclassname-string

Fullscreen detection with react styled-components

I'm using React and styled-components. I would like to hide the Navbar when user switch full screen mode by pressing F11 (Chrome).
I have tried following:
const NavbarContainer = styled.div`
height: 30px;
background-color: mediumseagreen;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
& :fullscreen {
display: none;
};
`
What else is needed to make it work? Currently the Navbar is still visible when I go fullscreen.
We have two options to resolve the problem with fullscreen mode.
First solution:
In styled-components you will need to use the #media all and (display-mode: fullscreen) instead of the :fullscreen pseudo-class, because this pseudo-class works only with Fullscreen API.
This will triggered with F11 key as an on/off switch, however we cann't use the Esc key to cancel the fullscreen mode.
const NavbarContainer = styled.div`
height: 30px;
background-color: mediumseagreen;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
#media all and (display-mode: fullscreen) {
display: none;
}
`;
Second solution:
We using Fullscreen API, when we will call this API after that the :fullscreen pseudo-class will works. This approach is good if you want to use Esc key to exit from fullscreen mode, also to assign another key (like Enter) or element (like button) to trigger fullscreen mode.
The Fullscreen API adds methods to present a specific Element (and its descendants) in fullscreen mode, and to exit fullscreen mode once it is no longer needed. This makes it possible to present desired content—such as an online game—using the user's entire screen, removing all browser user interface elements and other applications from the screen until fullscreen mode is shut off. MDN
Although, if we using #media query in our css instead of :fullscreen pseudo-class, we also can to use F11 key as a trigger. As a plus, with this approach, we will recive two different notifications about of exiting the fullscreen mode.
import { useEffect, useRef } from "react";
import styled from "styled-components";
const NavbarContainer = styled.div`
height: 30px;
background-color: mediumseagreen;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
#media all and (display-mode: fullscreen) {
display: none;
}
`;
const FullScreenButton = styled.button`
padding: 0.5rem 1rem;
#media all and (display-mode: fullscreen) {
display: none;
}
`;
export default function App() {
const refNavbar = useRef<HTMLDivElement>(null);
useEffect(() => {
const navbar = refNavbar.current;
const actionFn = (event: KeyboardEvent) => {
if ((event.key === 'Enter') && navbar) {
navbar.requestFullscreen();
return;
}
};
document.addEventListener('keyup', actionFn, false);
return () => {
document.removeEventListener('keyup', actionFn, false);
};
}, []);
const fullScreenOnClick = () => {
const navbar = refNavbar.current;
if (!navbar) return;
navbar.requestFullscreen();
};
return (
<div className="App">
<NavbarContainer ref={refNavbar}>Navigation</NavbarContainer>
<section>
<p>(User content!)</p>
<FullScreenButton onClick={fullScreenOnClick}>Fullscreen mode</FullScreenButton>
</section>
</div>
);
}
Caveat: Fullscreen API with assigned F11 key as trigger.
If you will try to assign the F11 key using the Fullscreen API to be able to exit also with the Esc key you will get strange behavior. It may be the cause of two different events Fullscreen API (with Esc key) and F11 key.
Fullscreen API issue with F11 and Esc buttons.
F11 key can exit programmatic-fullscreen, but programmatic-exitFullscreen cannot exit F11-fullscreen. Which is the problem you are running into. Also, Esc key cannot exit F11-fullscreen, but does exit programmatic-fullscreen. Fullscreen API not working if triggered with F11
Additionally:
Another interesting issue about :fullscreen pseudo-class. If you want to hide more than one element (like a button), this doesn't works as it will be bind to the current element. It's better to use the #media query.
Navbar and botton have a :fullscreen pseudo-class
The way it works:
const NavbarContainer = styled.div`
height: 30px;
background-color: blueviolet;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
#media all and (display-mode: fullscreen) {
display: none;
}
`;
The :fullscreen pseudo-class is used for a different purpose. Using javascript you can represent any DOM element to full-screen.
For example, if you will do the following thing:
document.querySelector('#navbar')?.requestFullscreen()
...then #navbar:fullscreen will work.
I think that was it
const NavbarContainer = styled.div`
height: 30px;
background-color: mediumseagreen;
padding: 10px 20px;
display: flex;
justify-content: space-between;
align-items: center;
color: white;
&::-webkit-full-screen {
display: none;
}
`

Effect to selected link gastby styled-component

I have a header and I want to mark the link currently by selecting.
With Styled-Component I must write something like: Const NavLink = styled (link) ... But with the properties of the NAV element there is one that is called ActiveClassname and it can be put on an already defined class, my question is how I define that Independent class with Styled-Component.
I would like to create a class that is called something like Linkactive and that can happen something so ActiveClassname = "Linkactive". But I have not been able to.
code of navMenu
<NavMenu>
{menuData.map((item, index) => (
<NavLink to={item.link} key={index} activeClassName="active">
{item.title}
</NavLink>
))}
</NavMenu>
code with styled-component
const NavMenu = styled.div`
display: flex;
align-items: center;
/* margin-right: -48px; */
#media screen and (max-width: 768px) {
display: none;
}`
const NavLink = styled(Link)`
color: #00286d;
display: flex;
align-items: center;
text-decoration: none;
padding: 0 1rem;
height: 100%;
cursor: pointer;
`
You can define static props/attributes using .attrs
This is a chainable method that attaches some props to a styled
component.
const NavLink = styled(Link).attrs(() => ({
activeClassName: "LinkActive",
}))`
color: #00286d;
display: flex;
align-items: center;
text-decoration: none;
padding: 0 1rem;
height: 100%;
cursor: pointer;
&.LinkActive {
// Apply active style here
}
`
If you wanted to use the activeClassName. You can create a class selector on the styling of your NavMenu.
That way, it would only affect .active class of direct/deep child nodes of your NavMenu
const NavMenu = styled.div`
display: flex;
align-items: center;
& .active {
// whatever styling you want
}
#media screen and (max-width: 768px) {
display: none;
}
`;

React modal component receiving same class

I have a react Modal component that I have made. I can pass a prop that makes it full screen or define a width. It all works fine, but i'm having an issue reusing it when the first modal is open. The 2nd component takes the same class as the 1st, so it makes the max-width 100%.
For example, I want to have a full screen modal, and show a 2nd modal at 50% size on top when i click a link.
I am using styled components which i think is where the issue may be happening. Im just not sure how to approach it.
Modal component:
<ModalContainer visible={visible} fullScreen={fullScreen}>
<ModalDialog role="dialog" width={width}>
<ModalBody>{children}</ModalBody>
</ModalDialog>
</ModalContainer>
Styles
export const ModalDialog = styled.div`
width: 100%;
max-width: ${({ width }) => width};
`;
export const ModalContainer = styled.div`
visibility: hidden;
opacity: 0;
display: none;
${({ visible }) =>
visible &&
css`
visibility: visible;
opacity: 1;
display: flex;
`}
${({ fullScreen }) =>
fullScreen &&
css`
width: 100vw;
height: 100vh;
overflow: none;
${ModalDialog} {
box-shadow: none;
max-width: 100%;
}
`}
`}
The class of the first modal for dialog is what is also being used for the 2nd even though that does not have the full screen prop.

Resources