Hey and welcome to this submit about animating a transition in React when a part is destroyed, AKA faraway from the DOM.
I normally face points when making use of some fadeIn/fadeOut transition on a React part or JSX factor. As a result of when the part/factor is faraway from the DOM I can now not apply any transition.
Let’s write some primary React app with a counter which is elevated by 1 each second.
We show the counter and under it we show a react picture at any time when the rely is divisible by 3.
Demo here.
// Disable React Strict mode for this instance
perform App() {
const [count, setCount] = useState(0);
const [isDivisibleByThree, setIsDivisibleByThree] = useState(false);
useEffect(() => {
if (rely !== 0 && rely % 3 === 0) setIsDivisibleByThree(true);
}, [count]);
useEffect(() => {
setInterval(() => {
setCount((p) => (p += 1));
}, 1000);
}, []);
return (
<div
model={{
show: "flex",
flexDirection: "column",
hole: "50px",
alignItems: "middle",
justifyContent: "middle",
}}
>
<div>
<h1>rely is {rely}</h1>
</div>
<p>
{isDivisibleByThree && (
<img ref={imageRef} src={reactLogo} alt="react emblem"/>
)}
</p>
</div>
);
}
We are able to animate at any time when the <img>
is seen with the next CSS.
This manner when the picture first seems it should have some slide-in animation, shifting from high to backside.
img {
animation: slide-in 0.5s ease-in-out;
}
p {
peak: 5rem;
width: 5rem;
}
@keyframes slide-in {
0% {
opacity: 0;
rework: translateY(-50%);
}
100% {
opacity: 1;
rework: translateY(0);
}
}
So… how can we animate when the <img>
factor is faraway from the DOM.
Let’s create a customized hook known as useHandleDestroyAnimated
, this hook accepts a ref (from the useRef
hook) from the HTML factor we want to animate on destroy.
Earlier than hiding the <img>
by way of setting the isDivisibleByThree state to false, we animate its destruction after 1000ms, then, we set the state to false.
async perform sleep(ms: quantity) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
perform useHandleDestroyAnimated<T extends HTMLElement>(
ref: MutableRefObject<T | null>
): [boolean, (_: boolean) => void] {
const [state, setState] = useState(false);
// everytime the state or ref change, if state is true, we animate the destroy of the part.
useEffect(() => {
if (state) {
handleDeletion(ref);
}
}, [ref, state]);
perform handleDeletion<T extends HTMLElement>(
factor: MutableRefObject<T | null>
) {
const model = factor?.present?.model;
if (!model) return;
sleep(1000).then(() => {
model.transition = "all 0.5s";
model.rework = "translateY(-50%)";
model.opacity = "0";
sleep(1000).then(() => {
setState(false);
});
});
}
return [state, setState];
}
Let’s add the ref from the useRef
hook to the App.tsx:
const imageRef = useRef<HTMLImageElement | null>(null);
.....
.....
.....
<p>
{isDivisibleByThree && (
<img ref={imageRef} src={reactLogo} alt="react emblem" />
)}
</p>
The ultimate code is:
import { useEffect, useState, MutableRefObject, useRef } from "react";
import reactLogo from "./belongings/react.svg";
import "./App.css";
async perform sleep(ms: quantity) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
const types = Object.freeze({
opacity: "0",
rework: "translateY(-50%)",
transition: "all 0.5s",
});
perform useHandleDestroyAnimated<T extends HTMLElement>(
ref: MutableRefObject<T | null>
): [boolean, (_: boolean) => void] {
const [state, setState] = useState(false);
useEffect(() => {
if (state) {
handleDeletion(ref);
}
}, [ref, state]);
perform handleDeletion<T extends HTMLElement>(
factor: MutableRefObject<T | null>
) {
const model = factor?.present?.model;
if (!model) return;
sleep(1000).then(() => {
model.transition = types.transition;
model.rework = types.rework;
model.opacity = types.opacity;
sleep(1000).then(() => {
setState(false);
});
});
}
return [state, setState];
}
perform App() {
const [count, setCount] = useState(0);
const imageRef = useRef<HTMLImageElement | null>(null);
const [isDivisibleByThree, setIsDivisibleByThree] =
useHandleDestroyAnimated(imageRef);
useEffect(() => {
if (rely !== 0 && rely % 3 === 0) setIsDivisibleByThree(true);
}, [count]);
useEffect(() => {
setInterval(() => {
setCount((p) => (p += 1));
}, 1000);
}, []);
return (
<div
model={{
show: "flex",
flexDirection: "column",
hole: "50px",
alignItems: "middle",
justifyContent: "middle",
}}
>
<div>
<span>rely is {rely}</span>
</div>
<p>
{isDivisibleByThree && (
<img ref={imageRef} src={reactLogo} alt="react emblem" />
)}
</p>
</div>
);
}
export default App;
I hope you discovered this convenient!
You’ll be able to comply with me on: