Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,15 @@
* @oncall react_native
*/

describe('AnimatedNode', () => {
describe('AnimatedValue', () => {
let NativeAnimatedHelper;
let AnimatedNode;

function createNativeAnimatedNode(): AnimatedNode {
class NativeAnimatedNode extends AnimatedNode {
__isNative = true;
__getNativeConfig(): {} {
return {};
}
}
return new NativeAnimatedNode();
let AnimatedValue;

function createNativeAnimatedValue(): AnimatedValue {
return new AnimatedValue(0, {useNativeDriver: true});
}

function emitMockUpdate(node: AnimatedNode, mockValue: number): void {
function emitMockUpdate(node: AnimatedValue, mockValue: number): void {
const nativeTag = node.__nativeTag;
expect(nativeTag).not.toBe(undefined);

Expand Down Expand Up @@ -50,15 +44,15 @@ describe('AnimatedNode', () => {

NativeAnimatedHelper =
require('../../../src/private/animated/NativeAnimatedHelper').default;
AnimatedNode = require('../nodes/AnimatedNode').default;
AnimatedValue = require('../nodes/AnimatedValue').default;

jest.spyOn(NativeAnimatedHelper.API, 'createAnimatedNode');
jest.spyOn(NativeAnimatedHelper.API, 'dropAnimatedNode');
});

it('emits update events for listeners added', () => {
const callback = jest.fn();
const node = createNativeAnimatedNode();
const node = createNativeAnimatedValue();
node.__attach();
const id = node.addListener(callback);

Expand All @@ -75,7 +69,7 @@ describe('AnimatedNode', () => {
});

it('creates a native node when adding a listener', () => {
const node = createNativeAnimatedNode();
const node = createNativeAnimatedValue();
node.__attach();
expect(NativeAnimatedHelper.API.createAnimatedNode).not.toBeCalled();

Expand All @@ -85,7 +79,7 @@ describe('AnimatedNode', () => {
});

it('drops a created native node on detach', () => {
const node = createNativeAnimatedNode();
const node = createNativeAnimatedValue();
node.__attach();
expect(NativeAnimatedHelper.API.createAnimatedNode).toBeCalledTimes(0);

Expand All @@ -100,7 +94,7 @@ describe('AnimatedNode', () => {

it('emits update events for listeners added after re-attach', () => {
const callbackA = jest.fn();
const node = createNativeAnimatedNode();
const node = createNativeAnimatedValue();
node.__attach();

node.addListener(callbackA);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default class AnimatedAddition extends AnimatedWithChildren {
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export default class AnimatedDiffClamp extends AnimatedWithChildren {

__attach(): void {
this._a.__addChild(this);
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export default class AnimatedDivision extends AnimatedWithChildren {
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -376,6 +376,7 @@ export default class AnimatedInterpolation<

__attach(): void {
this._parent.__addChild(this);
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ export default class AnimatedModulo extends AnimatedWithChildren {

__attach(): void {
this._a.__addChild(this);
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export default class AnimatedMultiplication extends AnimatedWithChildren {
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
super.__attach();
}

__detach(): void {
Expand Down
46 changes: 0 additions & 46 deletions packages/react-native/Libraries/Animated/nodes/AnimatedNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,11 @@
* @format
*/

import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
import type {PlatformConfig} from '../AnimatedPlatformConfig';

import NativeAnimatedHelper from '../../../src/private/animated/NativeAnimatedHelper';
import invariant from 'invariant';

const {startListeningToAnimatedNodeValue, stopListeningToAnimatedNodeValue} =
NativeAnimatedHelper.API;

type ValueListenerCallback = (state: {value: number, ...}) => mixed;

export type AnimatedNodeConfig = $ReadOnly<{
Expand All @@ -33,7 +29,6 @@ let _assertNativeAnimatedModule: ?() => void = () => {

export default class AnimatedNode {
#listeners: Map<string, ValueListenerCallback> = new Map();
#updateSubscription: ?EventSubscription = null;

_platformConfig: ?PlatformConfig = undefined;

Expand Down Expand Up @@ -78,9 +73,6 @@ export default class AnimatedNode {
);

this._platformConfig = platformConfig;
if (this.#listeners.size > 0) {
this.#ensureUpdateSubscriptionExists();
}
}

/**
Expand All @@ -93,9 +85,6 @@ export default class AnimatedNode {
addListener(callback: (value: any) => mixed): string {
const id = String(_uniqueId++);
this.#listeners.set(id, callback);
if (this.__isNative) {
this.#ensureUpdateSubscriptionExists();
}
return id;
}

Expand All @@ -107,9 +96,6 @@ export default class AnimatedNode {
*/
removeListener(id: string): void {
this.#listeners.delete(id);
if (this.__isNative && this.#listeners.size === 0) {
this.#updateSubscription?.remove();
}
}

/**
Expand All @@ -119,44 +105,12 @@ export default class AnimatedNode {
*/
removeAllListeners(): void {
this.#listeners.clear();
if (this.__isNative) {
this.#updateSubscription?.remove();
}
}

hasListeners(): boolean {
return this.#listeners.size > 0;
}

#ensureUpdateSubscriptionExists(): void {
if (this.#updateSubscription != null) {
return;
}
const nativeTag = this.__getNativeTag();
startListeningToAnimatedNodeValue(nativeTag);
const subscription: EventSubscription =
NativeAnimatedHelper.nativeEventEmitter.addListener(
'onAnimatedValueUpdate',
data => {
if (data.tag === nativeTag) {
this.__onAnimatedValueUpdateReceived(data.value);
}
},
);

this.#updateSubscription = {
remove: () => {
// Only this function assigns to `this.#updateSubscription`.
if (this.#updateSubscription == null) {
return;
}
this.#updateSubscription = null;
subscription.remove();
stopListeningToAnimatedNodeValue(nativeTag);
},
};
}

__onAnimatedValueUpdateReceived(value: number): void {
this.__callListeners(value);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,7 @@ export default class AnimatedObject extends AnimatedWithChildren {
const node = nodes[ii];
node.__addChild(this);
}
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export default class AnimatedProps extends AnimatedNode {
const node = nodes[ii];
node.__addChild(this);
}
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,7 @@ export default class AnimatedStyle extends AnimatedWithChildren {
const node = nodes[ii];
node.__addChild(this);
}
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export default class AnimatedSubtraction extends AnimatedWithChildren {
__attach(): void {
this._a.__addChild(this);
this._b.__addChild(this);
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export default class AnimatedTracking extends AnimatedNode {
let {platformConfig} = this._animationConfig;
this.__makeNative(platformConfig);
}
super.__attach();
}

__detach(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export default class AnimatedTransform extends AnimatedWithChildren {
const node = nodes[ii];
node.__addChild(this);
}
super.__attach();
}

__detach(): void {
Expand Down
68 changes: 66 additions & 2 deletions packages/react-native/Libraries/Animated/nodes/AnimatedValue.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
* @format
*/

'use strict';

import type {EventSubscription} from '../../vendor/emitter/EventEmitter';
import type {PlatformConfig} from '../AnimatedPlatformConfig';
import type Animation, {EndCallback} from '../animations/Animation';
import type {InterpolationConfigType} from './AnimatedInterpolation';
import type AnimatedNode from './AnimatedNode';
Expand Down Expand Up @@ -85,6 +85,9 @@ function _executeAsAnimatedBatch(id: string, operation: () => void) {
* See https://reactnative.dev/docs/animatedvalue
*/
export default class AnimatedValue extends AnimatedWithChildren {
#listenerCount: number = 0;
#updateSubscription: ?EventSubscription = null;

_value: number;
_startingValue: number;
_offset: number;
Expand Down Expand Up @@ -118,6 +121,67 @@ export default class AnimatedValue extends AnimatedWithChildren {
return this._value + this._offset;
}

__makeNative(platformConfig: ?PlatformConfig): void {
super.__makeNative(platformConfig);
if (this.#listenerCount > 0) {
this.#ensureUpdateSubscriptionExists();
}
}

addListener(callback: (value: any) => mixed): string {
const id = super.addListener(callback);
this.#listenerCount++;
if (this.__isNative) {
this.#ensureUpdateSubscriptionExists();
}
return id;
}

removeListener(id: string): void {
super.removeListener(id);
this.#listenerCount--;
if (this.__isNative && this.#listenerCount === 0) {
this.#updateSubscription?.remove();
}
}

removeAllListeners(): void {
super.removeAllListeners();
this.#listenerCount = 0;
if (this.__isNative) {
this.#updateSubscription?.remove();
}
}

#ensureUpdateSubscriptionExists(): void {
if (this.#updateSubscription != null) {
return;
}
const nativeTag = this.__getNativeTag();
NativeAnimatedAPI.startListeningToAnimatedNodeValue(nativeTag);
const subscription: EventSubscription =
NativeAnimatedHelper.nativeEventEmitter.addListener(
'onAnimatedValueUpdate',
data => {
if (data.tag === nativeTag) {
this.__onAnimatedValueUpdateReceived(data.value);
}
},
);

this.#updateSubscription = {
remove: () => {
// Only this function assigns to `this.#updateSubscription`.
if (this.#updateSubscription == null) {
return;
}
this.#updateSubscription = null;
subscription.remove();
NativeAnimatedAPI.stopListeningToAnimatedNodeValue(nativeTag);
},
};
}

/**
* Directly set the value. This will stop any animations running on the value
* and update all the bound properties.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1168,6 +1168,10 @@ declare export default class AnimatedValue extends AnimatedWithChildren {
constructor(value: number, config?: ?AnimatedValueConfig): void;
__detach(): void;
__getValue(): number;
__makeNative(platformConfig: ?PlatformConfig): void;
addListener(callback: (value: any) => mixed): string;
removeListener(id: string): void;
removeAllListeners(): void;
setValue(value: number): void;
setOffset(offset: number): void;
flattenOffset(): void;
Expand Down