How to make a react custom component animated using react-spring

629 Views Asked by At

I have a react custom component called HomeContent that accepts some props.

To make that animated as per the react-spring documentation I made

const animatedHomeContent = animated(HomeContent);

Then for rendering I did like this:

 <animatedHomeContent
    id={1}
    avatar="O"
    image="..."
    image_title="Onion"
    title=" Medium Sized Onions"
    subtitle="November 08, 2020"
    desc="Onions from farms of Nasik"
    price="74"
    quantity="1kg"
/>

But I see the props are not getting passed. Please tell me how to do it.

1

There are 1 best solutions below

0
On

It depends on how you want to interact with your component, do you want to use the api or simply supply updated props?

// update by regenerating springProps from some other state
const springProps = useSpring(props) 

// update springProps with api
const [springProps, api] = useSpring(() => props) 

Updating by regenerating the spring

With the first example, using one of the animated native elements (animated.XXX) you will rerender the animated wrapper every time you want to update the state, because you will pass it a new, updated springProps object every time you update it, which will cause it to rerender. Or... this is only partially true, the individual SpringValue's in the returned object will be the same with both methods so if you pass them as individual props, rerendering would theoretically not be necessary but if you pass them as a whole object (when springProps is a style object for example) it will be a changed object from last render and cause a rerender of the animated wrapper.

When you use a custom element that does not take a ref inside the wrapper instead of one of the native elements, the component will rerender once for every animation frame. This is suboptimal from a performance perspective, but with this strategy, you can use whichever api (prop names) you want for your component, much like you have done.

Here is a sandbox with this solution that works: sandbox

You can observe that the animation works but also that the component rerenders a lot.

Updating via the api

If you want to make it more efficient, and use the api for updating your custom component, you need to adhere to a few rules. When you use the api, react-spring updates the element via a ref on the corresponding DOM element, and therefore, react-spring must understand how to update the element without requiring React to rerender it. This implies:

  • Your custom element must be able to hold a ref
  • Your custom element must attach the ref to a DOM element on which you want all animations to take place. Because forwardRef doesn't allow you to add multiple refs, changes cannot take place on multiple DOM elements inside your custom component if you want to wrap it in animated (there are other strategies to solve this, such as NOT wrapping your component in animated and instead use native animated.XXX elements inside your component and pass SpringValues as props).
  • The property names of your custom component must correspond to the properties you want to update on the DOM element to which the ref its attached. Otherwise react-spring will not understand how to update this element.

Since your element has a lot of custom properties, react-spring won't be able to update your component via the api. It will attempt to do so by simply setting the updated properties on the element to which the ref is attached, but since you need React to map the custom properties to the actual properties on the DOM element, this will fail (no DOM element that I know of have the set of properties that you provide).

As an example of how to accomplish this, here is a sandbox showing the outlines of this strategy: sandbox

Here you can see that the component renders only once and can still be updated. You can also see that the properties used on the custom component wrapped in animated corresponds to props on a div element, enabling react-spring to do exactly what we want (the children of the AnimatedHomeContent is a special prop in React named props.children which react-spring knows how to deal with).

<AnimatedHomeContent style={{ backgroundColor: spring.backgroundColor }}>
    {spring.content}
</AnimatedHomeContent>

In the sandbox is also a bad example where other property names are used.

<AnimatedHomeContentBad
    backgroundColor={spring.backgroundColor}
    content={spring.content}
/>

To get a correct behaviour from this version, React has to process the component to map the input props to the native DOM element props. When updating this version via the api, react-spring does not rerender the component (since it can take a ref) and instead sets the updated properties on the element to which the ref is attached (inspect the element in the console to see for yourself that these props are set after updating). Since the properties are not real properties, nothing updates or animates on the component.