From 5257c35d05578bab0233254da81f1216f461f18d Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Mon, 6 Mar 2017 21:42:33 -0800 Subject: [PATCH] Add new `forkEvent`/`unforkEvent` API Reviewed By: vjeux Differential Revision: D4648427 fbshipit-source-id: 9bbbd81f49a9363ac271b3906d73f937f0d1f500 --- .../Animated/src/AnimatedImplementation.js | 52 ++++++++++++++++--- .../Animated/src/__tests__/Animated-test.js | 16 ++++++ 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/Libraries/Animated/src/AnimatedImplementation.js b/Libraries/Animated/src/AnimatedImplementation.js index 5cb8743fa..bbe47147f 100644 --- a/Libraries/Animated/src/AnimatedImplementation.js +++ b/Libraries/Animated/src/AnimatedImplementation.js @@ -2192,9 +2192,29 @@ function attachNativeEvent(viewRef: any, eventName: string, argMapping: Array { + typeof event === 'function' && event(...args); + listener(...args); + }; + } +} + +function unforkEvent(event: ?AnimatedEvent | ?Function, listener: Function): void { + if (event && event instanceof AnimatedEvent) { + event.__removeListener(listener); + } +} + class AnimatedEvent { _argMapping: Array; - _listener: ?Function; + _listeners: Array = []; _attachedEvent: ?{ detach: () => void, }; @@ -2205,7 +2225,9 @@ class AnimatedEvent { config?: EventConfig = {} ) { this._argMapping = argMapping; - this._listener = config.listener; + if (config.listener) { + this.__addListener(config.listener); + } this._attachedEvent = null; this.__isNative = shouldUseNativeDriver(config); @@ -2214,6 +2236,14 @@ class AnimatedEvent { } } + __addListener(callback: Function): void { + this._listeners.push(callback); + } + + __removeListener(callback: Function): void { + this._listeners = this._listeners.filter((listener) => listener !== callback); + } + __attach(viewRef, eventName) { invariant(this.__isNative, 'Only native driven events need to be attached.'); @@ -2228,7 +2258,7 @@ class AnimatedEvent { __getHandler() { if (this.__isNative) { - return this._listener; + return this._callListeners; } return (...args) => { @@ -2247,13 +2277,14 @@ class AnimatedEvent { traverse(mapping, args[idx], 'arg' + idx); }); } - - if (this._listener) { - this._listener.apply(null, args); - } + this._callListeners(...args); }; } + _callListeners = (...args) => { + this._listeners.forEach(listener => listener(...args)); + }; + _validateMapping() { const traverse = (recMapping, recEvt, key) => { if (typeof recEvt === 'number') { @@ -2602,5 +2633,12 @@ module.exports = { */ attachNativeEvent, + /** + * Advanced imperative API for snooping on animated events that are passed in through props. Use + * values directly where possible. + */ + forkEvent, + unforkEvent, + __PropsOnlyForTests: AnimatedProps, }; diff --git a/Libraries/Animated/src/__tests__/Animated-test.js b/Libraries/Animated/src/__tests__/Animated-test.js index be7838ecb..fc313a8af 100644 --- a/Libraries/Animated/src/__tests__/Animated-test.js +++ b/Libraries/Animated/src/__tests__/Animated-test.js @@ -353,6 +353,22 @@ describe('Animated tests', () => { expect(listener.mock.calls.length).toBe(1); expect(listener).toBeCalledWith({foo: 42}); }); + it('should call forked event listeners', () => { + var value = new Animated.Value(0); + var listener = jest.fn(); + var handler = Animated.event( + [{foo: value}], + {listener}, + ); + var listener2 = jest.fn(); + var forkedHandler = Animated.forkEvent(handler, listener2); + forkedHandler({foo: 42}); + expect(value.__getValue()).toBe(42); + expect(listener.mock.calls.length).toBe(1); + expect(listener).toBeCalledWith({foo: 42}); + expect(listener2.mock.calls.length).toBe(1); + expect(listener2).toBeCalledWith({foo: 42}); + }); }); describe('Animated Interactions', () => {