You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[bug]: Incorrect result type in SpringValue onChange handler #2183
The onChange option in the SpringValue constructor indicates that the argument is of type AnimationResult<SpringValue<number>>
Thus, result.value is indicated to be of type number, and logging this should display the animated value. However, it actually prints undefined as the spring animates:
If I instead log the entire result, I see the number.
The onChange callback of the SpringValue is triggered whenever the animation state updates, which includes both the start and end states of the animation. However, at the start of the animation, the value property of the animation result might not have been set yet, leading to undefined being logged.
If you want to log the animated value specifically when it changes, you should use the onRest callback instead. The onRest callback is triggered when the animation completes and the final value is available. Here's an example of how you might modify your code:
Thank you for the suggestion. You're correct that within onRest, result contains and AnimationResult as expected. I'm specifically trying to use onChange here though, as I want to read intermediate values throughout the transition.
The reason my example is printing undefined is because result contains the actual value (a number) in the onChange callback instead of an AnimationResult type, and trying to read the "value" property from a number is undefined.
The change I'm requesting is to ensure result holds an AnimationResult instead of a plain value to be consistent with the type signature and other callbacks.
Not an any leak — the type is already correct (result.value is number). It's a runtime/type mismatch: the SpringValue-level onChange is called with the raw value instead of an AnimationResult, so result.value is undefined.
protected_onChange(value: T,idle?: boolean){if(!idle){this._onStart()callProp(this.animation.onChange,value,this)// raw value}callProp(this.defaultProps.onChange,value,this)// raw valuesuper._onChange(value,idle)}
onChange(result) is handed 5, so result.value is (5).value → undefined. Logging the whole result shows 5 because result is the value itself.
onChange is the only event that does this. onStart and onRest already dispatch an AnimationResult (getFinishedResult/getCancelledResult), and the Controller-level onChange sets result.value before calling the handler. Only the SpringValue-level onChange skips it.
Fix
Wrap the value in an AnimationResult, mirroring onStart/onRest:
protected_onChange(value: T,idle?: boolean){constresult=getFinishedResult(value,false)// { value, finished: false, cancelled: false }if(!idle){this._onStart()callProp(this.animation.onChange,result,this)}callProp(this.defaultProps.onChange,result,this)super._onChange(value,idle)// leave raw — internal fluid observers depend on it}
Leave super._onChange(value) untouched: that emits the internal change event the Controller/animated tree subscribe to, which expects the raw value.
This aligns the runtime with the AnimationResult contract the type already advertises, so typed code keeps compiling — result.value just stops being undefined. Pair it with a runtime test asserting result.value matches the spring's current value during a change; the type side is covered by a .test-d guard.
Activity
Bilalshaikh15 commentedon Aug 9, 2023
The onChange callback of the SpringValue is triggered whenever the animation state updates, which includes both the start and end states of the animation. However, at the start of the animation, the value property of the animation result might not have been set yet, leading to undefined being logged.
If you want to log the animated value specifically when it changes, you should use the onRest callback instead. The onRest callback is triggered when the animation completes and the final value is available. Here's an example of how you might modify your code:
so if you log result.value now the number 1 or 0 would be printed in the console.
Please let me know did i fix th issue,
as i am new in contributing to open source can you tell me the project file location so i could commit changes.
mattrossman commentedon Aug 9, 2023
Thank you for the suggestion. You're correct that within onRest,
resultcontains and AnimationResult as expected. I'm specifically trying to use onChange here though, as I want to read intermediate values throughout the transition.The reason my example is printing undefined is because
resultcontains the actual value (a number) in the onChange callback instead of an AnimationResult type, and trying to read the "value" property from a number is undefined.I can get the output I want by logging the
resultitself: https://codesandbox.io/s/busy-hugle-5w6px4?file=/src/App.tsxThe change I'm requesting is to ensure
resultholds an AnimationResult instead of a plain value to be consistent with the type signature and other callbacks.incon commentedon Apr 3, 2025
I'm having the same Typescript type issues. The onChange value is a number type and not the AnimationResult<SpringValue> type.
sadan4 commentedon Oct 21, 2025
Just ran into this after wondering why nothing was working for a while. Would be nice to get this fixed.
joshuaellis commentedon Jun 19, 2026
Not an
anyleak — the type is already correct (result.valueisnumber). It's a runtime/type mismatch: theSpringValue-levelonChangeis called with the raw value instead of anAnimationResult, soresult.valueisundefined.SpringValue._onChange(packages/core/src/SpringValue.ts):onChange(result)is handed5, soresult.valueis(5).value→undefined. Logging the wholeresultshows5becauseresultis the value itself.onChangeis the only event that does this.onStartandonRestalready dispatch anAnimationResult(getFinishedResult/getCancelledResult), and theController-levelonChangesetsresult.valuebefore calling the handler. Only theSpringValue-levelonChangeskips it.Fix
Wrap the value in an
AnimationResult, mirroringonStart/onRest:Leave
super._onChange(value)untouched: that emits the internalchangeevent theController/animated tree subscribe to, which expects the raw value.This aligns the runtime with the
AnimationResultcontract the type already advertises, so typed code keeps compiling —result.valuejust stops beingundefined. Pair it with a runtime test assertingresult.valuematches the spring's current value during a change; the type side is covered by a.test-dguard.