Wrapping React Spring's useSpring Hook (A Use Case for Wrapping External Libraries)
Tags: javascript, react, webdev, tutorial
Published: 2021-12-21 21:45:18

[In my previous post](https://dev.to/michaelmangial1/wrapping-react-querys-usequery-a-use-case-for-wrapping-external-libraries-34ed), I discussed some potential improvements by wrapping React Query's `useQuery` hook.

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:

```jsx
// 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:

```jsx
// 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:

```jsx
// 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:

```jsx
// 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:

```jsx
// 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?