17
Wrapping React Spring's useSpring Hook (A Use Case for Wrapping External Libraries)
In this post, I'd like to aim for similar improvements with React Spring's useSpring
hook.
The useSpring
hook you to animate an element's style by controlling its style from when first appears to when the animation completes:
// some-component.js
import { animated, useSpring } from 'react-spring';
const spring = useSpring({
from: { opacity: 0 },
to: { opacity: 1 },
});
// ...
<animated.div style={spring}>Hello World</animated.div>
Without an abstraction, there is no way to make the animations reusable.
One way to make the animation reusable is to create a file that exports an object associating a { from, to, ...etc }
config (the argument that useSpring
takes) with an animation name:
// animations.js
export default {
fadeIn: {
from: { opacity: 0 },
to: { opacity: 1 },
},
};
// some-component.js
import { animated, useSpring } from 'react-spring';
import animations from './animations';
const spring = useSpring(animations.fadeIn);
// ...
<animated.div style={spring}>Hello World</animated.div>
This is the easiest solution, but we have to inconveniently import from two places.
We can improve upon this by export react-spring
modules plus our animations
object from a single file:
// animations.js
export const animations = {
fadeIn: {
from: { opacity: 0 },
to: { opacity: 1 },
},
};
export * from 'react-spring';
// some-component.js
import { animated, animations, useSpring } from './animations';
const spring = useSpring(animations.fadeIn);
// ...
<animated.div style={spring}>Hello World</animated.div>
We can improve upon this even more by not having to import animated
, animations
, and useSpring
, and then scope animations.fadeIn
to useSpring
.
Instead, we can expose use[AnimationName]
hooks that return all that we need:
// animations.js
import { animated, useSpring } from 'react-spring';
const animations = {
fadeIn: {
from: { opacity: 0 },
to: { opacity: 1 },
},
};
export function useFadeIn() {
const spring = useSpring(animations.fadeIn);
return {
animated,
spring,
};
}
// some-component.js
import { useFadeIn } from './animations';
const { animated, spring } = useFadeIn();
// ...
<animated.div style={spring}>Hello World</animated.div>
Alternatively to creating a hook for every animation, you could expose a more generic but similar useSpring
wrapper:
// animations.js
import { animated, useSpring as useBaseSpring } from 'react-spring';
const animations = {
fadeIn: {
from: { opacity: 0 },
to: { opacity: 1 },
},
};
export const PRESETS = Object.freeze(Object.keys(animations));
export function useSpring({ preset } = {}) {
const spring = useBaseSpring(animations[preset]);
return {
animated,
spring,
};
}
// some-component.js
import { PRESETS, useSpring } from './animations';
const { animated, spring } = useSpring({ preset: PRESETS.fadeIn });
// ...
<animated.div style={spring}>Hello World</animated.div>
Personally, I like creating a hook for every animation, just as you would create a CSS class to encapsulate a certain set of styles.
🎉 We've brainstormed ways to improve upon React Spring's useSpring
hook.
How would you wrap this hook?
17