[bug]: Calling .start() on a spring doesn't work if a re-render happens when using react-spring 10.0.1
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?
I'm using the imperative API to start a spring when a component is first mounted in my website. My component also re-renders at that time because a variable in a zustand store changes:
// This line is just here to cause a re-render of the component
const { causeReRender } = useReRenderStore();
useEffect(() => {
heightSpringAPI.start({ progress: 1.0 });
}, []);
The spring has always started properly until I switched from version 9.7.5 to version 10.0.1 of react-spring. Now it doesn't start at all.
To Reproduce
I have created a small repo that you can use to reproduce the bug: https://github.com/diegomacario/react-spring-bug
The repo simply animates a cube upwards when you open the website.
- The
mainbranch uses react-spring 9.7.5 and React 18. The spring starts properly with those versions. - There is a branch called
react-spring-10.0.1-with-react-18. The spring doesn't start with those versions. - There is a branch called
react-spring-10.0.1-with-react-19. The spring doesn't start with those versions.
Here are some deploys that allow you to compare:
- react-spring 9.7.5 + React 18 (works, you will see cube get raised): https://react-spring-cjvx6rmm8-diegomacarios-projects.vercel.app/
- react-spring 10.0.1 + React 18 (doesn't work, cube won't move): https://react-spring-2pol4ssaa-diegomacarios-projects.vercel.app
- react-spring 10.0.1 + React 19 (doesn't work, cube won't move): https://react-spring-aphisqa0p-diegomacarios-projects.vercel.app
Expected Behaviour
Springs should start regardless of whether re-renders happen or not, like they always did.
Link to repo
https://github.com/diegomacario/react-spring-bug
Ran into the same behavior. For some reason, it helps to explicitly mark the initial and pending values with from: and to:.
const [heightSpring, heightSpringAPI] = useSpring(
() => ({from: {
progress: 0.0,
...
}}),
[]
);
// Raise the cube when the website opens
useEffect(() => {
heightSpringAPI.start({to:{ progress: 1.0 }});
}, []);
Ran into the same behavior. For some reason, it helps to explicitly mark the initial and pending values with from: and to:.
const [heightSpring, heightSpringAPI] = useSpring( () => ({from: { progress: 0.0, ... }}), [] );// Raise the cube when the website opens useEffect(() => { heightSpringAPI.start({to:{ progress: 1.0 }}); }, []);
I was ripping my hair out with spring values resetting to 0, and this suggested change fixed it for me on v10.0.1. Thank you for saving my sanity.
To be clear, on v.9.7.5, this type of thing worked fine:
// initialize without "from"
const [springs, api] = useSprings(5, i => ({ left: 0 }));
// imperative api...
useEffect(() => {
api.start(i => ({ to: { left: i * 100 } }));
}, [api]);
On v10.0.1, to prevent left from resetting to 0 inexplicably, it must look like this:
// must initialize within "from"
const [springs, api] = useSprings(5, i => ({ from: { left: 0 } }));
// imperative api...
useEffect(() => {
api.start(i => ({ to: { left: i * 100 } }));
}, [api]);