How to trigger a CSS animation on EVERY TIME a react component re-renders
Add a key like this:
function Card({ cardText }) {
return <div key={cardText} className="roll-out">{cardText}<div/>
}
In your code, when the div re-renders, react only changes its inner text. Adding a key will make react think it's a different div when the key changes, so it'll unmount it and mount again.
How to add CSS animation to specific re-rendered item in React?
What you essentially want to do is restart a CSS animation for a particular element. To be honest, I'm not sure of a better way to do this in React than by accessing the DOM element with a reference.
You can do this by making each item its own component and using useRef
to get access to the DOM element you want to animate, and then apply the animation code in the linked stack overflow answer above.
const RenderItem = ({ item, handleIncrease }) => {
const stockElem = useRef()
const animate = () => {
let s = stockElem.current
s.style.animation = 'none'
// I had to do "let x =" here because my create-react-app config was yelling at me
let x = s.offsetHeight; // trigger reflow
s.style.animation = null
}
const increase = () => {
handleIncrease()
animate()
}
return (
<div>
<div>{item.fruit}</div>
<div ref={stockElem} className="add-color">{item.stock}</div>
<button onClick={increase}>increase</button>
<br />
<br />
</div>
)
}
And then in your main component, render each item:
return <RenderItem key={item.id} item={item} handleIncrease={handleIncrease} />;
The key
approach
There is a way to do this with key
, but I don't like it since it goes against key
's purpose as defined by React, and can probably lead to problems down the road.
return (
<div key={`${item.id}-${item.stock}`}>
<div>{item.fruit}</div>
<div className="add-color">{item.stock}</div>
<button onClick={handleIncrease}>increase</button>
<br />
<br />
</div>
);
The key line is <div key={`${item.id}-${item.stock}`}>
. React will un-mount and remount an element whose key changes. So, this utilizes the uniqueness of item.id
combined with the fact that item.stock
changes to rerender the div.
Again, even though the first solution I provided is more verbose, it's definitely more canonical.
How to add animations on every re-render of component
Just add something data to <h2>
-tag, then React can re-render this tag and animation started.
<h2 className="question" key={currentQuestion}>{questions[currentQuestion]}</h2>
React: Calling Render() during animation. What happens?
On this case CSSTransition
is keeping internal state to know in which phase of the animation it is.
React does not reset the state of the CSSTransition
instance because it knows it refers to the same element (React relies on the type of component and position on the tree to decide if it is the same element, or on the key
prop when defined)
Because the internal state of CSSTransition
is the same it's render method renders the same css class before and after the state change on your component.
See your example it working without flicker https://codesandbox.io/s/hopeful-shaw-c9ldr
CSSTransition
might change the state in response to a change in any of the props you pass to it (which in this case does not even occur) but that's dependant on the CSSTransition
implementation.
Finally, if you passed a different key
to CSSTransition
it would reset the animation as React would consider it a new component, so would unmount the previous one and mount a new one, with a new internal state.
see the example with the animation reseting here:
https://codesandbox.io/s/jolly-maxwell-2ihkf
re-triggering css animations with react
(Thanks again to @leo for this answer which helped me get to this one!)
I got the desired behavior (forcing the animation to restart) by
- Immediately removing the highlight class, and
- Setting a timeout (10msec seems to be fine) for adding back the highlight class.
The timeout seems to force react to separately render the component without the highlight class and then again with the highlight class, causing the animation to restart. (Without the timeout, react may collapse these changes into one render step, causing the DOM to treat the whole thing as a no-op.)
A nice result of this approach where each Item
manages its own highlight state is that multiple items can be highlighted at the same time (e.g. if updates for one item come in before the highlight fades on another).
Demo here
const Item = ({ id, updateTime }) => {
const [showHighlight, setShowHighlight] = React.useState(false);
// By putting `updateTime` in the dependency array of `useEffect,
// we re-trigger the highlight every time `updateTime` changes.
useEffect(() => {
if (updateTime) {
setShowHighlight(false);
setTimeout(() => {
setShowHighlight(true);
}, 10);
}
}, [updateTime]);
return <div className={showHighlight ? "updated" : ""}>Item {id}</div>;
};
const App = () => {
// tracking the update times at the top level
const [updateTimes, setUpdateTimes] = React.useState({});
// ...
<Item key={id} id={id} updateTime={updateTimes[id]} />
// ...
<button
onClick={() => {
setUpdateTimes({ ...updateTimes, [id]: Date.now() });
}}
>
// ...
}
My animation is not working when re rendering my react component?
You could make use of keys that react is using to determine whether something has changed. This means that your render method should look something like this:
import shortid from "shortid";
getRandomKey = () => {
return shortid.generate();
}
render() {
return (
<div
key={this.getRandomKey()}
className={cs({
"tls-forms": true,
"tls-forms--large": this.props.type === "S",
"tls-forms--medium tls-forms--login": !(this.props.type === "S")
})}
>
// content here
</div>);
}
Since you need to run animation on each render, you'll need to generate some random key every time (that's why we are calling this.getRandomKey()
on each render). You can use whatever you like for your getRandomKey implementation, though shortid is pretty good for generating unique keys.
React 17.0.2 Functional Component Only Plays CSS Animation on First Render
During re-render react looks for only the elements which have changed. If not, then it doesn't update the element, even if it rerenders.
So you can either make react know that this element have changed, or unmount and mount the component again. I have provided both the usecases.
- You can add a
key
prop with a random value, so that react knows its different on each re-render and hence remounts the component. This is quick and hacky and it works.
const UserFeedBackComponent = (props) => {
console.log("UserFeedBackComponent rendered");
const rand = Math.random();
return (
<div className={classes.Border} key={rand}>
<div className={classes.CorrectAnswer}>Correct!</div>
</div>
);
};
https://codesandbox.io/s/reactcssanimationissue-forked-9jufd
- Another way would be to remove it from the dom after its invisible. The logic will be handled by its parent,
InputComponent
import React, { useState } from "react";
import UserFeedBackComponent from "../UserFeedBack/UserFeedBackComponent";
import classes from "./InputComponent.module.css";
const InputComponent = () => {
const [currentInput, setCurrentInput] = useState("");
const [showNotification, setShowNotification] = useState(false);
const inputHandler = (event) => {
setCurrentInput(event.target.value);
setShowNotification(true);
setTimeout(() => {
setShowNotification(false);
}, 3000);
};
return (
<React.Fragment>
<input type="text" value={currentInput} onChange={inputHandler} />
<div className={classes.Output}>{"Output is " + currentInput}</div>
{showNotification && <UserFeedBackComponent text={currentInput} />}
</React.Fragment>
);
};
export default InputComponent;
Every time you need to show the notification, you just need to set showNotification
to true
, and set it to false
again via setTimeout
which will fire after the duration of your animation i.e 3s. This is much better because, there's no stale invisible element, polluting your dom. You can still iterate and improve upon it.
https://codesandbox.io/s/reactcssanimationissue-forked-mejre
How to replay a CSS3 animation in Reactjs
ReactJS uses key
attribute to distinguish between elements in the diffing algorithm. So, the first time your component loads , react assumes it as new element and renders it accordingly. But next time it tries to render the same element with different data , react feels that it has the same structure and only replaces the new data with old data.
So to apply animations for every change, we need to tell react that this is a new component. This can be done by providing a key
to the element with your animation class. And key can be randomly generated using any random number generator( I generally use shortid
).
For working example, please refer: this
Related Topics
Safari Image Size Auto Height CSS
How to Make Elements Flow from Bottom to Top
Why 'Float:Left' Doesn't Work with a Fixed Width
How to Make a Angled Arrow Like This with Gradient and Transparent
How to Create Circle with Four Quarters
Max-Height: X% Doesn't Work on Chrome
Grails 2.3 Changes CSS Font-Face Url to "Resource:/..."
Toggle Class with CSS Not Jquery
Strange Scrollbars Around the Svg Background in Chrome
Input Background Colour Destroys Styling
What Is Element.Style and Why Is It Overriding My CSS Settings
Disappearing Position Fixed Header in iOS7 Mobile Safari
Precedence in CSS Selector Specifity Conflicts (Type VS Class Selector)
How to Override Bootstrap 3 Styles with External Custom CSS
CSS Performance Between Class and Attribute Selectors