CSS transition not working when changing react state - reactjs

I want to see animation when I change width of my sidebar. Material UI is used here (AppBar). I think it's because React just re-renders component quickly. How you would do it? I think it would work if i just changed class or added class.
Also it' necessary to understand that content of StyledAppBar will depend on whether it's opened. So I will need re-render so that the content change
Additional question - do you know how to make AppBar render as tag instead oh tag? :)
const Header = () => {
const [open, setOpen] = useState(true);
const StyledAppBar = styled(AppBar)`
background-color: red;
height: 100vh;
width: ${open ? '240px' : '50px'};
transition: width 1s ease-in-out;
position: fixed;
left: 0;
`;
return (
<StyledAppBar>
<Toolbar>
<Typography>MUI SHOP</Typography>
<ShoppingBasket onClick={() => setOpen(prev => !prev)}/>
</Toolbar>
</StyledAppBar>
);
};

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

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!

Changing font size with respect to the change in screen resolution using styled-components in react

I'm designing a simple react app using styled components. I'm trying to make it more responsive.
So I created a component(Ad) with props values of width and height.
Depending upon the width and height values, the font size should change.
This is how made my Ad component.
const AdContainer = styled.div`
max-width: ${props=>props.width};
max-height: ${props=>props.height};
`;
const Adp = styled.p`
font-size: ${props=>props.width>"870px"?"24px":"16px"};
`;
function Ad({height,width}) {
return (
<AdContainer height={height} width={width}>
<AdTitle>Hello</AdTitle>
</AdContainer>
);
}
Consider this parent component
function ProductPage() {
return (
<>
<Ad width={"1920px"} height={"600px"}/>
</>
);
}
export default ProductPage;
When we pass width=1920px and height=600px, the font size of Adtitle should change into 24px because of this
const Adp = styled.p`
font-size: ${props=>props.width>"870px"?"24px":"16px"};
`;
But it is not changing and sticks to 16px.
What can I do to rerender this component whenever the screen size changes?
Or is there any other alternatives to solve this so that wherever I use this Ad component, the font size should change with respect to the given props width and height value?
You are trying to compare string to string as numbers. Do not do like this. Do it like this.
const Adp = styled.p`
font-size: ${p => p.width > 870 ? "24px" : "16px"};
`;
// And pass the props like this
<Adp width={1920} height={600}/>
// AdContainer
const AdContainer = styled.div`
max-width: ${p => `${p.width}px`};
max-height: ${p => `${p.height}px`};
`;

styled-components conditional props doesn't work in Material-ui components

I'd like to set fade-in, fade-out animation on the Grid component using Material-UI and styled-components. But it doesn't work and there is an error about the conditional prop. Could you tell me how to do that, please?
import React from "react";
import styled, { keyframes } from "styled-components";
import { Grid } from "#material-ui/core";
const fadeIn = keyframes`
0% {
height: 0
}
50% {
height: 50%;
}
100% {
height: 100%;
}
`;
const fadeOut = keyframes`
0% {
height: 100%
}
50% {
height: 50%;
}
100% {
height: 0;
}
`;
const AnimationGrid = styled(Grid)<{ isOpen: boolean }>`
&& {
visibility: ${props => (props.isOpen ? "visible" : "hidden")};
animation: ${props => (props.isOpen ? fadeIn : fadeOut)}
0.3s linear 0s 1 forwards;
}
`;
type AnimationProps = {
isOpen: boolean;
};
const AnimationComp = ({isOpen}: AnimationProps) => {
return (
<AnimationGrid container isOpen={isOpen}>
<Grid item xs={6}>
Here is left section
</Grid>
<Grid item xs={6}>
Here is right section
</Grid>
</AnimationGrid>
)
}
Also there is a error on console. Fade-in animation is fine but fade-out doesn't work.
Warning: React does not recognize the `isOpen` prop on a DOM element.
If you intentionally want it to appear in the DOM as a custom attribute,
spell it as lowercase `isopen` instead. If you accidentally passed it from a parent component,
remove it from the DOM element.
I'd say isOpen prop doesn't work well. Thank you.
Your question consists of 2 parts, so I'll answer it that way.
For the console error: this behavior has been bugging many people. The reason you see this error, is because the property isOpen is passed to the underlying DOM element. However, DOM elements don't support a boolean attribute isOpen.
To work around this, you can use
styled(({ isOpen, ...props }) => <Grid {...props} />)<{ isOpen: boolean }>`...`
For the animation part: you don't see the fade-out animation, because you set visibility: hidden at the start of it. This way, the element disappears immediately. To get similar behavior to visibility: hidden,
you could add opacity: 0 to your animation at '100%'
and set pointer-events: ${props => props.isOpen ? 'initial' : 'none'}.
Hope this helps!

Resources