React: Animate ternary output components - css

I'm surprised by the issue im having with this simple animation procedure. I have a ternary that displays one series of components or another based on a condition. My first thought was to wrap them in a div that animates them once they appear. Unfortunately, its only animating the first condition on page load, switching tabs has no effect. Any thoughts?
const dashboard = () => {
const [pane, setPane] = useState(0);
const props = useSpring({ opacity: 1, from: { opacity: 0 } });
return (
...
<MainContent>
{pane === 0 ? (
<animated.div style={props}>
<LastModuleWidget {...mockLastModule} />
<BookmarkedModules />
<LatestActivity />
</animated.div>
) : (
<animated.div style={props}>
<StatsModule />
<PreviouslyCompleted />
</animated.div>
)}
</MainContent>
...
)};
Moving the animation wrapper immediately outside of the ternary had no effect.
I've tried it with Springs (above) and my own implementation, no effect.

Ok, so the core issue is that my MainContent container did not have a key prop.
<MainContent key={pane}>
{pane === 0 ? (
<_AnimateFadeIn>
<LastModuleWidget {...mockLastModule} />
<BookmarkedModules />
<LatestActivity />
</_AnimateFadeIn>
) : (
<_AnimateFadeIn>
<BookmarkedModules />
<LatestActivity />
</_AnimateFadeIn>
)}
</MainContent>
also, for simplicity i reverted back to using my own animation since its a simple fade in:
const _AnimateFadeIn = styled.div`
#keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
animation: fadeIn 0.7s;
`;
I'm using styled-components. If you want to recreate this in CSS just write something like:
div className="_animateFadeIn" or whatever. Same thing.

Related

Apply svg inline styling for rotating an icon

I am importing an icon from the library and would like to rotate it by 90 deg in my code to make it look like my requirement. I am trying to apply transform attribute but its not working. It's working fine when I try that attribute in developer tools.
<IconButton
flat
primary
icon={
isAllExpanded ? (
<ChevronRightDoubleIcon width={16} color={ICON_COLOR} transform='rotate(90deg)'/>
) : (
<ChevronLeftDoubleIcon color={ICON_COLOR} />
)
}
/>
I also tried to wrap it in a separate div element and apply css styling but that changes all the icons in the page even though I wrap the div element around this Iconbutton.
<DoubleArrow>
<IconButton
flat
primary
icon={
isAllExpanded ? (
<ChevronRightDoubleIcon width={16} color={ICON_COLOR} />
) : (
<ChevronLeftDoubleIcon color={ICON_COLOR} />
)
}
/>
</DoubleArrow>
export const DoubleArrow = styled.div`
#ChevronRightDoubleIcon {
svg {
transform: rotate(90deg);
}
`;
Is there a way to apply svg transform styling when I use the icon tag that I need ?

Reactjs slide animation problem without unknown target dimensions

In my react app I'm doing some kind of animations to make it nicer. I want to make a div appear by sliding from above disappear by sliding it back.
I'm using bootstrap 5.1.0 and react-bootstrap 1.6.1 in my stack.
I came up with a solution that is to wrap the content inside a div and toggle its maxHeight between window.innerHeight and 0 as needed. I added also a custom css class
.transition-height{
transition: max-height 0.5s ease-in-out;
}
Problem is that obviosly, when I close the div, the animation starts from a very hight value of maxHeight and so there's a delay between the close trigger event and the moment when you see the div actually closing. I used window.innerHeight as I don't have a known height target value to use, it depends from the length of div content.
I uploaded the code to codesandbox, so you can see its behaviour.
How can I avoid this?
In your case just need to get the height of the div with useRef and when will show = true set the current height. codesandbox exapmle
ApperDiv
const AppearDiv = (props) => {
let { show = false, className = "", style = {} } = props;
const ref = useRef(null);
const getPaperBoxHeight = ref.current?.scrollHeight || 0;
const filteredProps = Object.entries(props)
.filter(([k, _]) => !["className", "show", "style"].includes(k))
.reduce((p, [k, v]) => ({ ...p, [k]: v }), {});
style = show ? { height: getPaperBoxHeight } : { height: 0 };
return (
<div
ref={ref}
className={
"overflow-hidden transition-height" + (className && ` ${className}`)
}
style={style}
{...filteredProps}
/>
);
};

Vuetify form with validation component shows on page load despite v-if

I have a compnent (custom styled button) that I want to show when the form is successfully validated using vuetify :rules.
However somehow when using transition in order to smoothly show the component, on initial page load it displays that component briefly for a second then it fades out. It does not happen if I dont use transition though (it remains invisible when the page loads).
It works normally after that but I dont want it to briefly appear on initial page load.
Here is the form:
<v-form
ref="form"
v-model="valid"
:lazy-validation="lazy"
>
<v-text-field
v-model="sepalLength"
label="Sepal Length"
required
:rules="numberRule"
></v-text-field>
<transition name="fade">
<new-button v-if="valid"></new-button>
</transition>
</v-form>
And the css for the transition
<style scoped>
.fade-enter-active, .fade-leave-active {
transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8 */ {
opacity: 0;
}
</style>
EDIT, here is data model for validation:
data: () => ({
valid: true,
numberRule: [
v => !!v || 'Required',
v => (!isNaN(parseFloat(v))) || 'Must be float']
}),

How to animate conditional elements in React

I have a React application. I am using React Spring for overall animations. I am not able to animate 2 things -
The animation I am experimenting with is a simple opacity animation.
import { useSpring, animated } from "react-spring";
/***
Some code
***/
const styleProps = useSpring({
to: { opacity: 1 },
from: { opacity: 0 }
});
1) Is conditional elements. Please refer code below -
<section>
{!flag ? (
<animated.div style={styleProps}>
Some random text
</animated.div>
) : (
<animated.div style={styleProps}>
To appear with animation
</animated.div>
)
}
</section>
The issue is that the animated.div of react-spring does not animate the same. What is the right way? Is there a way to animate the same without react-spring?
2) I have a conditional bootstrap className attached based on a flag. I want to animate the same
<animated.div style={styleProps} className={classnames({
"col-lg-6": !flag,
"col-lg-12": flag
})}
>
Random Content
</animated.div>
For this also, the issue is that it is not animating. What is the right way?
Yo have a lot of question. I can answer part of it and maybe you will understand it better.
Your example of useSpring animation is triggered only once. And when you switch between components with the conditional render it will no longer animate.
But you can re-trigger the animation in useSpring, if you change the 'to' parameter conditionally (and leave the render to react-spring).
const styleProps1 = useSpring({
to: { opacity: flag ? 1 : 0 },
from: { opacity: 0 }
});
const styleProps2 = useSpring({
to: { opacity: flag ? 0 : 1 },
from: { opacity: 0 }
});
<section>
<>
<animated.div style={styleProps1}>
Some random text
</animated.div>
<animated.div style={styleProps2}>
To appear with animation
</animated.div>
</>
</section>
You have to use absolute positioning if you want the element to appear in the same place.
You can achieve similar effect with useTranstion also with absolute positioning. In this case the element dismounted at the end of animation. So if you have mouse click problems with the useSpring method you can try to switch to useTransition.
Maybe it also answer your second questiona as well. I am not familiar with bootstrap.

React - responsive app - better to hide element in component via CSS or not render it

I'm currently working on a React.js-based app. Lets say we have a Header component in React with a small logo component inside it that should only be displayed at mobile resolution levels. I'm passing an isMobile prop from the parent component. This prop is based on:
const mql = global.matchMedia(`(min-width: 768px)`);
mql.addListener(() => this._mediaQueryChanged());
this.setState({
mql: mql,
isMobile: !mql.matches
});
_mediaQueryChanged() {
this.setState({
isMobile: !this.state.mql.matches
});
}
<Header isMobile={this.state.isMobile} />
And in Header:
render() {
const {isMobile} = this.props;
const containerClass = classNames('header-component', {
'is-mobile': isMobile
});
return (
<header className={containerClass}>
{
isMobile &&
(
<section className="mobile-header">
<Button className="toggle-menu" onClick={() => this._toggleMenu()}>
<Icon name="menu" />
</Button>
<Logo className="header-logo" />
</section>
)
}
<span>login</span>
</header>
);
}
and if mql matches then it is not mobile. My question is: should I pass this param and re-render the component every time we change Media queries? During re-render hide/show logo component? Or just use CSS to show/hide it and the component will be mounted there all the time. No re-renders tho.
Thoughts?
I agree this may be a job for just CSS.
.header-class .logo {
display:none;
}
#media only screen and (min-width: 768px) {
.header-class .logo {
display:block;
}
}
I think it depends on what you're trying to achieve. If you have universal rendering with critical path CSS extraction and trying to shave milliseconds go for the react-only solution. Otherwise css-only solution will do but equally as well as the react-only solution because you're probably re-rendering already. That sneaky isMobile has just triggered a render somewhere else in your codebase (or will in the future); plus, you've just lost the extensibility of that react offers.

Resources