Reputation: 43
The following code is a simple react component that uses react-spring to animate the p
(animated.p
) elements. The animation is defined in the fadeIn
constant and is assigned to the animated.p
elements via the style={fadeIn}
tags.
//returns and animates <p> elements:
function QuoteBox(props) {
const fadeIn = useSpring({ opacity: 1, from: { opacity: 0 } });
return (
<>
<blockquote>
<animated.p id="text" style={fadeIn}>{props.quote}</animated.p>
</blockquote>
<animated.p id="author" style={fadeIn}>{props.author}<cite><em>{props.from}</em></cite></animated.p>
</>
);
}
When the page loads and the QuoteBox
component is mounted into the DOM, useSpring
changes the opacity of the animated.p
elements from 0 to 1.
However, I need this to happen every time I pass new props
to the animated.p
elements, but I'm struggling to understand how to do that.
There are some simple examples on the react-spring docs page, which uses the state
(toggle
) of the component to change the opacity from 0 to 1 and visa-versa, but it's not clear to me how to change the opacity from 0 to 1 every time I pass a new prop
to the animated.p
elements.
I'm just trying to obtain a simmple text fade-in when it changes.
Sorry for the previous confused question, this may be a little too difficult for me. Also, I'm not entirely sure react-spring is a good choice, it seems overly complicated for what I'm trying to achieve.
Upvotes: 3
Views: 1703
Reputation: 9354
You can achieve this using the updater function syntax. It is initialised with a function which returns the initial properties, and returns an updater function (as well as a stop
function, not used here) which allows the initial properties to be overwritten:
function QuoteBox(props) {
const [fadeIn, set] = useSpring(() => ({ opacity: 0 }));
React.useEffect(() => {
set({ opacity: 1, from: { opacity: 0 }});
}, [set, props.quote, props.author, props.from]);
return (
<>
<blockquote>
<animated.p id="text" style={fadeIn}>
{props.quote}
</animated.p>
</blockquote>
<animated.p id="author" style={fadeIn}>
{props.author}
<cite>
<em>{props.from}</em>
</cite>
</animated.p>
</>
);
This will effectively reset the animation on mount and for every subsequent rerender where the props change. React (or ESlint) will probably complain about the above use of useEffect
since none of the prop values are actually getting used, we're just watching them for changes since it seems implied that that's when the animation should rerun.
You could potentially avoid this with:
Memoisation:
const MemoQuoteBox = React.memo(QuoteBox);
Since all the props are strings (a simple data type which is equal by value, not by reference like objects or arrays) you can memoise the component as it is and it will only rerender when one of them changes. This will allow you to remove the dependency array since now the component is only going to update when a prop changes:
React.useEffect(() => {
set({ opacity: 1, from: { opacity: 0 }});
});
If you're going to pass more props to this component in the future it might not be ideal.
Reference:
const currString = props.quote + props.author + props.from;
const prevString = React.useRef(currString);
React.useEffect(() => {
if (currString === prevString.current) return;
set({ opacity: 1, from: { opacity: 0 }});
prevString.current = currString;
}, [set, currString]);
Now that we're using the passed value ESlint shouldn't throw up a warning. I've taken advantage of the fact the props are all strings to concat them here (since they are conceptually linked) but the principle would be the same if you kept them separate.
Dirty Return:
Just return the values directly from the effect and ESlint should consider them "used" although at this point you might as well just use eslint-disable-next-line
:
React.useEffect(() => {
set({ opacity: 1, from: { opacity: 0 }});
return props.quote + props.author + props.from;
}, [set, props.quote, props.author, props.from]);
Upvotes: 3