React js onclick issue - reactjs

I am trying to create an custom Radio button like feature. By putting onClick on div to change the state value and I am not able to change the same. Please help me understand what I am missing on.
const [category, setCategory] = useState("");
const handleSubmit = e => {
console.log(e);
setCategory(e.target.value);
};
return (
<>
<CenterAlignedColumnContainer>
<FormHeadingText>
<FormSectionHeadingTextContainer>
Category
</FormSectionHeadingTextContainer>
</FormHeadingText>
<CategoryContainer>
<TextRadioButton
value="Salads"
onClick={handleSubmit}
name="category"
>
<RadioButtonText>
<TextContainer>Salads</TextContainer>
</RadioButtonText>
</TextRadioButton>
<TextRadioButton value="Pasta" onClick={handleSubmit} name="category">
<RadioButtonText>
<TextContainer>Pasta</TextContainer>
</RadioButtonText>
</TextRadioButton>
</CategoryContainer>
<PartialWidthDivider />
</CenterAlignedColumnContainer>
</>
);
}
Styled div for custom Radio button
export const TextRadioButton = styled.div`
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 10px;
background: rgba(176, 167, 230, 0.5);
box-shadow: 0px 4px 4px rgba(176, 167, 230, 0.5);
border-radius: 20px;
margin-left: -10px;
margin: 8px;
`;

div does not have a value property, so you should reference it i.e. as a passed prop:
<TextRadioButton value='something' onClick={handleSubmit(props.value)} />
and use it explicitly
handleSubmit(value) {
setCategory(value);
}

change your onClick method to :
onClick={(e)=>handleSubmit(e}
or
onClick={
(e)=>{
console.log(e);
setCategory(e.target.value);
};

So there's a couple of problems wrong with this, this first and foremost being that you have a pseudo-element covering your entire display:
export const Background = styled.div`
width: 100%;
&:before {
display: block;
position: absolute;
width: 100%;
height: 400vh;
content: "";
background: url(https://res.cloudinary.com/antilibrary/image/upload/v1595154947/Piatto/backgroundFormNew_sk7yie.svg)
repeat center center;
opacity: 0.5;
}
`;
When an element is :before it means it is above any further elements in the stacking context, the same way that hard-coded elements are. A simple solution is to give the pseudo-element z-index: -1, which will ensure that it is placed behind everything else. Ideally, however, you would not use a pseudo-element at all, but instead a distinct div placed absolutely behind everything.
The second is that your buttons are not using good semantic HTML, and as a result you will not be able to target value, which does not exist on divs. Additionally, since the text of the button is separate from the button itself, there will be cases where it returns undefined anyway as the target of the event does not hold the button's value.
What I suggest you do first is change TextRadioButton to be a styled.button, you may need to adjust some of your styles to accommodate this. Then, change the event handler to the following:
const handleSubmit = e => {
const value = e.currentTarget.value;
setCategory(value);
};
The currentTarget of an event is the element where the event is registered, not whichever child element actually triggered it, which you access with regular target. This way, no matter which part of the button is clicked on, it will always reference the value on the top level of the button component.

Related

How to change other element when hover by using 'styled'

I'm new to Mui and trying to apply animation to components.
What I want to do is, I have four same component and each has its own image.
<MyComponent>some images...</MyComponent>
<MyComponent>some images...</MyComponent>
<MyComponent>some images...</MyComponent>
<MyComponent>some images...</MyComponent>
const MyComponent = styled("div")((theme) => ({
//... some styles.
// scale up when hovered
'&:hover': {
transform: "scale(1.2)",
marginRight: "20px",
}
}));
If I hover a <MyComponent>, I want to scale up hovered one, and scale down others.
Is there any ways to defined such action by using styled???
I would set a state then use a conditional to change styles .. IE
const [hoverState, setHoverState] = useState(false);
<MyComponent
onMouseOver={setHoverState(true)}
onMouseOut={setHoverState(false)}
style={hoverState ? {transform: "scale(1.2)",marginRight: "20px",} : ''}
>
some images...
</MyComponent>
You can do this with css and styled-components.
Note that the styled function is not meant to be called directly, but passed a template literal. Inside the template literal, you can write regular css expressions.
codesandbox
const MyComponent = styled.div`
transform: scale(1);
margin-right: 0px;
:hover {
transform: scale(1.2);
margin-right: 20px;
}
`;

Styled Components Injects wrong classes on wrong elements

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 :"

I want the arrow icon to flip up and down every time the state changes.and I want to animate it

I am using react, styled-components.
When state(visible) is set to true, DropMenu box1 and box2 will be displayed.
We want the ArrowDown icon to flip upward when state is true, and downward when false.
I also want to apply an animation when flipping it.
I want to add an animation like the Dropdown in the following site.  
Reference site
code
import "./styles.css";
import styled from "styled-components";
import React, { useState, useCallback } from "react";
import { ArrowDown } from "./ArrowDown";
const Item = styled.div<{ active?: boolean }>`
height: 40px;
width: 300px;
padding: 0px 30px;
&:hover {
background: #fafbfb;
}
`;
const DropMenu = styled.div`
display: flex;
align-items: center;
justify-content: space-between;
color: #899098;
width: 100%;
height: 100%;
font-size: 14px;
font-weight: bold;
gap: 12px;
:hover {
color: gray;
}
div {
display: flex;
align-items: center;
gap: 12px;
}
`;
const DropText = styled.div`
padding-left: 32px;
`;
export const App = () => {
const [visible, setVisible] = useState(false);
const handleDropVisibleChange = useCallback(() => {
setVisible((prevVisible) => !prevVisible);
}, [visible]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<h2>Start editing to see some magic happen!</h2>
<Item onClick={handleDropVisibleChange}>
<DropMenu>
<div>
<span>Menu</span>
</div>
<ArrowDown />
</DropMenu>
</Item>
{visible && (
<div style={{ transition: "all 0.5s ease" }}>
<Item>
<DropMenu>
<DropText>box1</DropText>
</DropMenu>
</Item>
<Item>
<DropMenu>
<DropText>box2</DropText>
</DropMenu>
</Item>
</div>
)}
</div>
);
};
export default App;
TLDR
Change your MenuItem component warpper to something like
const DropMenuWrapper = styled.div<{ visible: boolean }>`
transition: all 0.5s ease;
opacity: ${(props) => (props.visible ? 1 : 0)};
`;
replace the visibility switch mechanism with following
- {visible && (
- <div style={{ transition: "all 0.5s ease" }}>
+ <DropMenuWrapper visible={visible}>
similar action can be added to the arrow-down icon also with style
(The ArrowDown SVG icon must accept style if it is custom written component)
<ArrowDown
style={{
transition: "all 0.5s ease",
transform: `rotate(${visible ? 0 : "0.5turn"})`
}}
/>
Why this happened:
When a component (sub-component/element) is mounted in react, it starts a complete life cycle toward browser paint.
So it is must have the property which causes the element to animate, for example, I added the opacity transition to the example itself, forcing it to animate in the first look and in disappearing.
Although it comes with some performance cost of having unseen elements still in the dom (but not visible), making it bad for accessibility too, it is the simplest way to achieve this behavior.
Consider this example If you have an animated element, does it show the animation if you refresh the browser if the answer is yes, it will show animation in react too.
Another way of doing some animation in react.
Using third-party library react-transtion-group which is heavily used in lots of packages e.g. Material-UI.
In this case you can also trigger the end event and start to unmount the component as the animation disappears and end completely.
Using framer motion
If you want to take your understanding of what is needed for the transition when the component is unmounted and removed from aka dom, I highly encourage you to read the animation section of svelte docuementation
What I did, what might look stupid to more advanced developers was implement a simple check that would switch icons.
Note: This doesn't have an animation, though. It's just a simple switcharoo
define state in component
const [isOpen, setIsOpen] = useState(false);
Check whether icon is open or closed, if open, ExpandLessIcon, if closed ExpandMoreIcon.
<ExpandLessIcon
onClick={() => {
setIsOpen(!isOpen);
}}
/>
) : (
<ExpandMoreIcon
onClick={() => {
setIsOpen(!isOpen);
}}
/>
)}
The way it works is, once clicked, it'll just flip the true false state over and over, which in turn will change icons.

REACT my custom crosshair does not activate onclick functions

I created a custom crosshair using react and I can't seem to get it click on certain elements such as a button it won't let me click the button but If I removed the custom crosshair the button onclick will let me click and it will run the function associated with the button I tried creating a zIndex css to have the cursor overlapped the other crosshair but that did not work any suggestion to make it where I can click other elements and the function can run? written via codepen
const { useRef } = React
const Main = props => {
const cursor = {
cursor: "crosshair",
width: "20PX",
height: "20PX",
position: "fixed",
};
const borders = {
width: "800px",
height: "500px",
backgroud: "#ccc",
border: "4px solid #333"
}
const textInput = useRef()
React.useEffect(() => {
document.addEventListener("mousemove", (event) =>{
textInput.current.style.top = event.clientY + 'px'
textInput.current.style.bottom = event.clientX + 'px'
textInput.current.style.left = event.clientX + 'px'
textInput.current.style.right = event.clientY + 'px'
})
console.log("dsadas")
});
return (<div style={borders} >
<div ref={textInput} style={cursor}>
</div> <button >dsa2222ads</button></div>)
}
ReactDOM.render(<Main />, document.getElementById("root"));
The code in the question, while ingenious, cannot be fixed as-is, because of 2 main reasons:
The cursor element is receiving all click events instead of what's beneath it;
As pointed out in comments, my initial suggestion (to use pointer-events: none; styling on the cursor element) does make the button work properly, but the cursor: crosshair; style stops working because the element gets ignored when it comes to ALL pointer aspects, not only events.
The following alternative relies purely on CSS, more specifically on the * selector to assign the cursor: crosshair; style to all elements at any depth inside the containing element (and also place it on the borders element). The markup gets lighter since we don't need the cursor element any more and no more code to move it around the page. Also no more document-level event listeners.
.borders {
cursor: crosshair;
width: 800px;
height: 500px;
background: #ccc;
border: 4px solid #333;
}
.borders * {
cursor: crosshair;
}
<div class="borders">
<button onClick="alert('Ok!')">Button</button>
</div>
Now, should you need a different cursor on just some internal elements, you can easily achieve that using selector specificity to target that element and put cursor: auto; or whatever you need on it. If you want to restore the crosshair on that element's children, just re-apply the * selector, and so on. So you have complete and granular control over the cursor all with CSS. If a code sample for this scenario is needed, let me know in the comments. Thank you!

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