diff --git a/packages/react-native/Libraries/Animated/nodes/AnimatedColor.js b/packages/react-native/Libraries/Animated/nodes/AnimatedColor.js index 712c259d72ca..170da220ac09 100644 --- a/packages/react-native/Libraries/Animated/nodes/AnimatedColor.js +++ b/packages/react-native/Libraries/Animated/nodes/AnimatedColor.js @@ -139,6 +139,14 @@ export default class AnimatedColor extends AnimatedWithChildren { nativeColor: ?NativeColorValue; _suspendCallbacks: number = 0; + _listeners: { + [key: string]: { + r: string, + g: string, + b: string, + a: string, + }, + } = {}; constructor(valueIn?: InputValue, config?: ?AnimatedColorConfig) { super(config); @@ -171,6 +179,35 @@ export default class AnimatedColor extends AnimatedWithChildren { } } + addListener(callback: ColorListenerCallback): string { + const id = String(Math.random()); + const jointCallback = () => callback(this.__getValue()); + this._listeners[id] = { + r: this.r.addListener(jointCallback), + g: this.g.addListener(jointCallback), + b: this.b.addListener(jointCallback), + a: this.a.addListener(jointCallback), + }; + return id; + } + + removeListener(id: string): void { + if (!this._listeners[id]) { + // Already removed (e.g. after __detach / removeAllListeners) — safe no-op + return; + } + this.r.removeListener(this._listeners[id].r); + this.g.removeListener(this._listeners[id].g); + this.b.removeListener(this._listeners[id].b); + this.a.removeListener(this._listeners[id].a); + delete this._listeners[id]; + } + + removeAllListeners(): void { + Object.keys(this._listeners).forEach(id => this.removeListener(id)); + this._listeners = {}; + } + /** * Directly set the value. This will stop any animations running on the value * and update all the bound properties.