Skip to content

Commit

Permalink
refactor: JavaScript NativeReanimatedModule
Browse files Browse the repository at this point in the history
  • Loading branch information
tjzel committed Sep 30, 2024
1 parent b78c2e6 commit 3c08411
Show file tree
Hide file tree
Showing 28 changed files with 178 additions and 95 deletions.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type {
ValueRotation,
ShareableRef,
LayoutAnimationBatchItem,
IReanimatedModule,
} from '../commonTypes';
import { checkCppVersion } from '../platform-specific/checkCppVersion';
import { jsVersion } from '../platform-specific/jsVersion';
Expand All @@ -13,11 +14,15 @@ import { getValueUnpackerCode } from '../valueUnpacker';
import { isFabric } from '../PlatformChecker';
import type React from 'react';
import { getShadowNodeWrapperFromRef } from '../fabricUtils';
import ReanimatedModule from '../specs/NativeReanimatedModule';
import { ReanimatedTurboModule } from '../specs';
import { ReanimatedError } from '../errors';

export function createNativeReanimatedModule() {
return new NativeReanimatedModule();
}

// this is the type of `__reanimatedModuleProxy` which is injected using JSI
export interface NativeReanimatedModule {
export interface ReanimatedModuleProxy {
makeShareableClone<T>(
value: T,
shouldPersistRemote: boolean,
Expand Down Expand Up @@ -77,8 +82,8 @@ See \`https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshoo
}
}

export class NativeReanimated {
private InnerNativeModule: NativeReanimatedModule;
class NativeReanimatedModule implements IReanimatedModule {
#reanimatedModuleProxy: ReanimatedModuleProxy;

constructor() {
// These checks have to split since version checking depend on the execution order
Expand All @@ -87,8 +92,7 @@ export class NativeReanimated {
}
global._REANIMATED_VERSION_JS = jsVersion;
if (global.__reanimatedModuleProxy === undefined) {
const valueUnpackerCode = getValueUnpackerCode();
ReanimatedModule?.installTurboModule(valueUnpackerCode);
ReanimatedTurboModule?.installTurboModule(getValueUnpackerCode());
}
if (global.__reanimatedModuleProxy === undefined) {
throw new ReanimatedError(
Expand All @@ -99,38 +103,38 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti
if (__DEV__) {
checkCppVersion();
}
this.InnerNativeModule = global.__reanimatedModuleProxy;
this.#reanimatedModuleProxy = global.__reanimatedModuleProxy;
}

makeShareableClone<T>(
value: T,
shouldPersistRemote: boolean,
nativeStateSource?: object
) {
return this.InnerNativeModule.makeShareableClone(
return this.#reanimatedModuleProxy.makeShareableClone(
value,
shouldPersistRemote,
nativeStateSource
);
}

scheduleOnUI<T>(shareable: ShareableRef<T>) {
return this.InnerNativeModule.scheduleOnUI(shareable);
return this.#reanimatedModuleProxy.scheduleOnUI(shareable);
}

executeOnUIRuntimeSync<T, R>(shareable: ShareableRef<T>): R {
return this.InnerNativeModule.executeOnUIRuntimeSync(shareable);
return this.#reanimatedModuleProxy.executeOnUIRuntimeSync(shareable);
}

createWorkletRuntime(name: string, initializer: ShareableRef<() => void>) {
return this.InnerNativeModule.createWorkletRuntime(name, initializer);
return this.#reanimatedModuleProxy.createWorkletRuntime(name, initializer);
}

scheduleOnRuntime<T>(
workletRuntime: WorkletRuntime,
shareableWorklet: ShareableRef<T>
) {
return this.InnerNativeModule.scheduleOnRuntime(
return this.#reanimatedModuleProxy.scheduleOnRuntime(
workletRuntime,
shareableWorklet
);
Expand All @@ -142,7 +146,7 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti
iosReferenceFrame: number,
handler: ShareableRef<(data: Value3D | ValueRotation) => void>
) {
return this.InnerNativeModule.registerSensor(
return this.#reanimatedModuleProxy.registerSensor(
sensorType,
interval,
iosReferenceFrame,
Expand All @@ -151,23 +155,23 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti
}

unregisterSensor(sensorId: number) {
return this.InnerNativeModule.unregisterSensor(sensorId);
return this.#reanimatedModuleProxy.unregisterSensor(sensorId);
}

registerEventHandler<T>(
eventHandler: ShareableRef<T>,
eventName: string,
emitterReactTag: number
) {
return this.InnerNativeModule.registerEventHandler(
return this.#reanimatedModuleProxy.registerEventHandler(
eventHandler,
eventName,
emitterReactTag
);
}

unregisterEventHandler(id: number) {
return this.InnerNativeModule.unregisterEventHandler(id);
return this.#reanimatedModuleProxy.unregisterEventHandler(id);
}

getViewProp<T>(
Expand All @@ -181,50 +185,52 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti
shadowNodeWrapper = getShadowNodeWrapperFromRef(
component as React.Component
);
return this.InnerNativeModule.getViewProp(
return this.#reanimatedModuleProxy.getViewProp(
shadowNodeWrapper,
propName,
callback
);
}

return this.InnerNativeModule.getViewProp(viewTag, propName, callback);
return this.#reanimatedModuleProxy.getViewProp(viewTag, propName, callback);
}

configureLayoutAnimationBatch(
layoutAnimationsBatch: LayoutAnimationBatchItem[]
) {
this.InnerNativeModule.configureLayoutAnimationBatch(layoutAnimationsBatch);
this.#reanimatedModuleProxy.configureLayoutAnimationBatch(
layoutAnimationsBatch
);
}

setShouldAnimateExitingForTag(viewTag: number, shouldAnimate: boolean) {
this.InnerNativeModule.setShouldAnimateExitingForTag(
this.#reanimatedModuleProxy.setShouldAnimateExitingForTag(
viewTag,
shouldAnimate
);
}

enableLayoutAnimations(flag: boolean) {
this.InnerNativeModule.enableLayoutAnimations(flag);
this.#reanimatedModuleProxy.enableLayoutAnimations(flag);
}

configureProps(uiProps: string[], nativeProps: string[]) {
this.InnerNativeModule.configureProps(uiProps, nativeProps);
this.#reanimatedModuleProxy.configureProps(uiProps, nativeProps);
}

subscribeForKeyboardEvents(
handler: ShareableRef<number>,
isStatusBarTranslucent: boolean,
isNavigationBarTranslucent: boolean
) {
return this.InnerNativeModule.subscribeForKeyboardEvents(
return this.#reanimatedModuleProxy.subscribeForKeyboardEvents(
handler,
isStatusBarTranslucent,
isNavigationBarTranslucent
);
}

unsubscribeFromKeyboardEvents(listenerId: number) {
this.InnerNativeModule.unsubscribeFromKeyboardEvents(listenerId);
this.#reanimatedModuleProxy.unsubscribeFromKeyboardEvents(listenerId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
'use strict';
import { createJSReanimatedModule } from './js-reanimated';
import { shouldBeUseWeb } from '../PlatformChecker';
import { createNativeReanimatedModule } from './NativeReanimated';

export const ReanimatedModule = shouldBeUseWeb()
? createJSReanimatedModule()
: createNativeReanimatedModule();
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
'use strict';
// this file was created to prevent NativeReanimated from being included in the web bundle
import { createJSReanimatedModule } from './js-reanimated';
export const ReanimatedModule = createJSReanimatedModule();
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,23 @@ import {
isJest,
isWeb,
isWindowAvailable,
} from '../PlatformChecker';
import type { ShareableRef, Value3D, ValueRotation } from '../commonTypes';
import { SensorType } from '../commonTypes';
} from '../../PlatformChecker';
import { SensorType } from '../../commonTypes';
import type {
IReanimatedModule,
ShareableRef,
Value3D,
ValueRotation,
} from '../../commonTypes';
import type { WebSensor } from './WebSensor';
import { mockedRequestAnimationFrame } from '../mockedRequestAnimationFrame';
import type { WorkletRuntime } from '../runtimes';
import { logger } from '../logger';
import { ReanimatedError } from '../errors';
import { mockedRequestAnimationFrame } from '../../mockedRequestAnimationFrame';
import type { WorkletRuntime } from '../../runtimes';
import { logger } from '../../logger';
import { ReanimatedError } from '../../errors';

export function createJSReanimatedModule() {
return new JSReanimated();
}

// In Node.js environments (like when static rendering with Expo Router)
// requestAnimationFrame is unavailable, so we use our mock.
Expand All @@ -21,7 +30,7 @@ const requestAnimationFrameImpl =
? mockedRequestAnimationFrame
: globalThis.requestAnimationFrame;

export default class JSReanimated {
class JSReanimated implements IReanimatedModule {
nextSensorId = 0;
sensors = new Map<number, WebSensor>();
platform?: Platform = undefined;
Expand Down Expand Up @@ -290,7 +299,7 @@ export default class JSReanimated {
}
}

enum Platform {
export enum Platform {
WEB_IOS = 'web iOS',
WEB_ANDROID = 'web Android',
WEB = 'web',
Expand Down
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
'use strict';
import JSReanimated from './JSReanimated';
import type { StyleProps, AnimatedStyle } from '../commonTypes';
import type { StyleProps, AnimatedStyle } from '../../commonTypes';
import {
createReactDOMStyle,
createTransformValue,
createTextShadowValue,
} from './webUtils';
import { PropsAllowlists } from '../propsAllowlists';
import { logger } from '../logger';
import { ReanimatedError } from '../errors';
import { PropsAllowlists } from '../../propsAllowlists';
import { logger } from '../../logger';
import { ReanimatedError } from '../../errors';

const reanimatedJS = new JSReanimated();
export { createJSReanimatedModule } from './JSReanimated';

// TODO: Install these global functions in a more suitable location.
global._makeShareableClone = () => {
throw new ReanimatedError(
'_makeShareableClone should never be called in JSReanimated.'
'`_makeShareableClone` should never be called from React runtime.'
);
};

global._scheduleOnJS = () => {
throw new ReanimatedError(
'_scheduleOnJS should never be called in JSReanimated.'
'`_scheduleOnJS` should never be called from React runtime.'
);
};

global._scheduleOnRuntime = () => {
throw new ReanimatedError(
'_scheduleOnRuntime should never be called in JSReanimated.'
'`_scheduleOnRuntime` should never be called from React runtime.'
);
};

Expand All @@ -51,6 +51,7 @@ export interface ReanimatedHTMLElement extends HTMLElement {
removedAfterAnimation?: boolean;
}

// TODO: Move these functions outside of index file.
export const _updatePropsJS = (
// eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents
updates: StyleProps | AnimatedStyle<any>,
Expand Down Expand Up @@ -168,5 +169,3 @@ const updatePropsDOM = (
function isNativeProp(propName: string): boolean {
return !!PropsAllowlists.NATIVE_THREAD_PROPS_WHITELIST[propName];
}

export default reanimatedJS;
6 changes: 3 additions & 3 deletions packages/react-native-reanimated/src/Sensor.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
'use strict';
import NativeReanimatedModule from './NativeReanimated';
import { ReanimatedModule } from './ReanimatedModule';
import type {
SensorConfig,
SharedValue,
Expand Down Expand Up @@ -52,7 +52,7 @@ export default class Sensor {
) {
const config = this.config;
const sensorType = this.sensorType;
this.sensorId = NativeReanimatedModule.registerSensor(
this.sensorId = ReanimatedModule.registerSensor(
sensorType,
config.interval === 'auto' ? -1 : config.interval,
config.iosReferenceFrame,
Expand All @@ -75,7 +75,7 @@ export default class Sensor {

unregister() {
if (this.sensorId !== null && this.sensorId !== -1) {
NativeReanimatedModule.unregisterSensor(this.sensorId);
ReanimatedModule.unregisterSensor(this.sensorId);
}
this.sensorId = null;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/react-native-reanimated/src/UpdateProps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,11 @@ import type {
AnimatedStyle,
} from './commonTypes';
import type { Descriptor } from './hook/commonTypes';
import type { ReanimatedHTMLElement } from './js-reanimated';
import { _updatePropsJS } from './js-reanimated';
import { isFabric, isJest, shouldBeUseWeb } from './PlatformChecker';
import { runOnUIImmediately } from './threads';
import { ReanimatedError } from './errors';
import { _updatePropsJS } from './ReanimatedModule/js-reanimated';
import type { ReanimatedHTMLElement } from './ReanimatedModule/js-reanimated';

let updateProps: (
viewDescriptors: ViewDescriptorsWrapper,
Expand Down
Loading

0 comments on commit 3c08411

Please sign in to comment.