react-spring icon indicating copy to clipboard operation
react-spring copied to clipboard

[bug]: Spring reference seem to be shared between instances of the same component when using imperative API

Open terrymun opened this issue 5 months ago • 0 comments

Which react-spring target are you using?

  • [x] @react-spring/web
  • [ ] @react-spring/three
  • [ ] @react-spring/native
  • [ ] @react-spring/konva
  • [ ] @react-spring/zdog

What version of react-spring are you using?

10.0.1

What's Wrong?

The spring reference seems to be shared between renders, when using a callback with useSpring() (needed for access to imperative API). This bug is only reproducible after upgrading from 10.0.0 → 10.0.1, and the only possible explanation is due to this one-liner change here: https://github.com/pmndrs/react-spring/compare/v10.0.0...v10.0.1#diff-e1288b0efcce202f0575295aec86c6f33c5d81adac9751131dc5d2b08e40e73a

The only logical change between tehse two versions is that the updates.current ??= [] line was removed, which I suspect have triggered this behavior.

To Reproduce

Use the following contrived example: the <Toggle> component will tween a value randomly assigned to it using react-spring, but when clicking between different toggles, the previously clicked button value will also be mutated as the spring is erroneously run:

https://codesandbox.io/p/sandbox/7cz48k (uses 10.0.1, broken)

import { ComponentPropsWithRef, useEffect } from "react";
import { animated, useSpring, to } from "@react-spring/web";

type Props = Omit<ComponentPropsWithRef<"button">, "value"> & {
  value: number;
};

export const Button = ({ value, ...props }: Props) => {
  // NOTE: This is a VERY contrived example, as with the simplified code it is easier not to use a callback
  const [springs, api] = useSpring(() => ({
    ratio: 0,
  }));

  useEffect(() => {
    api.stop().start({
      ratio: value,
    });
  }, [api, value]);

  return (
    <animated.button type="button" {...props} value={springs.ratio}>
      {to([springs.ratio], (r) => r.toFixed(3))}
    </animated.button>
  );
};

https://github.com/user-attachments/assets/a6bcf261-4f7b-4749-935b-62edcfe19b40

The same code does not have this incorrect behavior when downgrading back to 10.0.0:

https://codesandbox.io/p/sandbox/2vclkz (uses 10.0.0, works)

https://github.com/user-attachments/assets/5a1eafda-de15-44d8-818a-5aeab81468d7

Expected Behaviour

The spring instance should not be shared between difference instances of the same component.

Link to repo

  • Working example when using 10.0.0: https://codesandbox.io/p/sandbox/2vclkz
  • Broken example when using 10.0.1: https://codesandbox.io/p/sandbox/7cz48k

terrymun avatar May 24 '25 15:05 terrymun