<div id="app"></div>
body {
color: #def;
font-family: sans-serif;
background-color: #1d1e22;
}
main {
padding: 4rem 2rem;
display: flex;
align-items: center;
gap: 2rem;
}
form {
flex: 2;
display: flex;
align-items: center;
justify-content: center;
gap: 1rem;
}
.card {
flex: 1;
border: 2px solid #def;
border-radius: 6px;
padding: 2rem;
}
.blink {
animation: blink 0.35s;
}
@keyframes blink {
0% {
background-color: #f0f0;
}
25% {
background-color: #f0f8;
}
100% {
background-color: #f0f0;
}
}
const blink = (el) => {
el.classList.add('blink');
setTimeout(() => {
el.classList.remove('blink');
}, 350);
};
const useBlink = (blinkOnProp, blinkOnRender, ref, prop) => {
React.useEffect(() => {
if (blinkOnProp) {
blink(ref.current);
}
}, [prop]);
React.useEffect(() => {
if (blinkOnRender) {
blink(ref.current);
}
});
};
const Child = ({ handler, blinkOnProp, blinkOnRender, description }) => {
const ref = React.useRef(null);
useBlink(blinkOnProp, blinkOnRender, ref, handler);
return (
<div ref={ref} className="card" onClick={handler}>
{description}
</div>
);
};
const Parent = () => {
const [_, setSymbol] = React.useState(Symbol());
const [blink, setBlink] = React.useState(true);
const test = () => {
setSymbol(Symbol());
};
const handleSelectChange = (e) => {
setBlink(e.target.value === 'onProp');
}
const handleClick = () => {};
return (
<>
<main>
<Child
description="Child component receiving inline handler"
handler={() => {}}
blinkOnProp={blink}
blinkOnRender={!blink}
/>
<Child
description="Child component receiving handler saved as a variable"
handler={handleClick}
blinkOnProp={blink}
blinkOnRender={!blink}
/>
</main>
<footer>
<form>
<select onChange={handleSelectChange}>
<option value="onProp">Blink on prop change</option>
<option value="onRender">Blink on render</option>
</select>
<button onClick={test} type="button">
Test
</button>
</form>
</footer>
</>
);
};
ReactDOM.render(
<Parent />,
document.getElementById('app'),
);
View Compiled
This Pen doesn't use any external CSS resources.