From d115ab2a520cff74b67691863556cd46fcd0d697 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Hanno=20J=2E=20G=C3=B6decke?= Date: Fri, 23 Jan 2026 18:34:21 +0100 Subject: [PATCH] Revert "Fix ios new arch scroll perf (#28)" This reverts commit 05d414d6d07fc04ebdaddb80946dd2b9162461b7. --- .../cpp/reanimated/Fabric/PropsRegistry.cpp | 61 +--- .../cpp/reanimated/Fabric/PropsRegistry.h | 13 - .../Fabric/ReanimatedCommitHook.cpp | 6 - .../reanimated/Fabric/ShadowTreeCloner.cpp | 2 - .../NativeModules/ReanimatedModuleProxy.cpp | 27 -- .../NativeModules/ReanimatedModuleProxy.h | 9 - .../ReanimatedModuleProxySpec.cpp | 7 - .../NativeModules/ReanimatedModuleProxySpec.h | 1 - .../react-native-reanimated/src/Colors.ts | 4 +- .../src/PropsRegistryGarbageCollector.ts | 79 ------ .../src/ReanimatedModule/NativeReanimated.ts | 9 - .../js-reanimated/JSReanimated.ts | 7 - .../ReanimatedModule/reanimatedModuleProxy.ts | 3 - .../src/common/constants/platform.ts | 22 -- .../src/common/style/processors/colors.ts | 263 ------------------ .../src/common/types/index.ts | 51 ---- .../src/commonTypes.ts | 6 - .../createAnimatedComponent/commonTypes.ts | 2 - .../createAnimatedComponent.tsx | 46 +-- 19 files changed, 8 insertions(+), 610 deletions(-) delete mode 100644 packages/react-native-reanimated/src/PropsRegistryGarbageCollector.ts delete mode 100644 packages/react-native-reanimated/src/common/constants/platform.ts delete mode 100644 packages/react-native-reanimated/src/common/style/processors/colors.ts delete mode 100644 packages/react-native-reanimated/src/common/types/index.ts diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/PropsRegistry.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/PropsRegistry.cpp index 59adb80e2ea7..74cb8b35192f 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/PropsRegistry.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/PropsRegistry.cpp @@ -1,4 +1,3 @@ -#define RCT_NEW_ARCH_ENABLED 1 #ifdef RCT_NEW_ARCH_ENABLED #include @@ -10,30 +9,9 @@ std::lock_guard PropsRegistry::createLock() const { return std::lock_guard(mutex_); } -#ifdef RCT_NEW_ARCH_ENABLED void PropsRegistry::update( const std::shared_ptr &shadowNode, - folly::dynamic &&props, - double timestamp) { - const auto tag = shadowNode->getTag(); - const auto it = map_.find(tag); - if (it == map_.cend()) { - // we need to store ShadowNode because `ShadowNode::getFamily` - // returns `ShadowNodeFamily const &` which is non-owning - map_[tag] = std::make_pair(shadowNode, props); - timestampMap_[shadowNode->getTag()] = timestamp; - } else { - // no need to update `.first` because ShadowNode's family never changes - // merge new props with old props - it->second.second.update(props); - - timestampMap_[shadowNode->getTag()] = timestamp; - } -} -#else -void PropsRegistry::update( - const std::shared_ptr &shadowNode, - folly::dynamic &&props) { + folly::dynamic &&props) { const auto tag = shadowNode->getTag(); const auto it = map_.find(tag); if (it == map_.cend()) { @@ -46,7 +24,6 @@ void PropsRegistry::update( it->second.second.update(props); } } -#endif // RCT_NEW_ARCH_ENABLED void PropsRegistry::for_each(std::function>> updates; - - for (const auto &[viewTag, pair] : map_) { - if (timestampMap_.at(viewTag) < timestamp) { - updates.emplace_back(viewTag, std::cref(pair.second)); - } - } - - const jsi::Array array(rt, updates.size()); - size_t i = 0; - for (const auto &[viewTag, styleProps] : updates) { - const jsi::Object item(rt); - item.setProperty(rt, "viewTag", viewTag); - item.setProperty(rt, "styleProps", jsi::valueFromDynamic(rt, styleProps.get())); - array.setValueAtIndex(rt, i++, item); - } - - return jsi::Value(rt, array); -} - -void PropsRegistry::removeUpdatesOlderThanTimestamp(const double timestamp) { - for (auto it = timestampMap_.begin(); it != timestampMap_.end();) { - const auto viewTag = it->first; - const auto viewTimestamp = it->second; - if (viewTimestamp < timestamp) { - it = timestampMap_.erase(it); - map_.erase(viewTag); - } else { - it++; - } - } -} -#endif // RCT_NEW_ARCH_ENABLED - } // namespace reanimated #endif // RCT_NEW_ARCH_ENABLED diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/PropsRegistry.h b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/PropsRegistry.h index df4b255892af..d34b42ac0622 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/PropsRegistry.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/PropsRegistry.h @@ -20,16 +20,9 @@ class PropsRegistry { std::lock_guard createLock() const; // returns a lock you need to hold when calling any of the methods below - #ifdef RCT_NEW_ARCH_ENABLED - void update( - const std::shared_ptr &shadowNode, - folly::dynamic &&props, - double timestamp); - #else void update( const std::shared_ptr &shadowNode, folly::dynamic &&props); - #endif // RCT_NEW_ARCH_ENABLED void for_each(std::function isPaused_; std::atomic shouldCommitAfterPause_; - std::unordered_map timestampMap_; // viewTag -> timestamp, protected by `mutex_` }; } // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp index aec192591663..c0a69c3ed2ae 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ReanimatedCommitHook.cpp @@ -68,12 +68,6 @@ RootShadowNode::Unshared ReanimatedCommitHook::shadowTreeWillCommit( return newRootShadowNode; } - #ifdef RCT_NEW_ARCH_ENABLED - if (commitOptions.source != ShadowTreeCommitSource::React) { - return newRootShadowNode; - } - #endif // RCT_NEW_ARCH_ENABLED - // ShadowTree not commited by Reanimated, apply updates from PropsRegistry reaShadowNode->unsetReanimatedMountTrait(); RootShadowNode::Unshared rootNode = newRootShadowNode; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ShadowTreeCloner.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ShadowTreeCloner.cpp index b13caa4089c1..95684c3cdf5d 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ShadowTreeCloner.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/Fabric/ShadowTreeCloner.cpp @@ -23,8 +23,6 @@ Props::Shared mergeProps( const auto &propsVector = it->second; auto newProps = shadowNode.getProps(); -// [note for piaskowyk]: I tried to remove this changes since we already have similar fixes from under the FORCE_REACT_RENDER_FOR_SETTLED_ANIMATIONS flag, -// but removing these changes causes noticable performance degradation #ifdef ANDROID if (propsVector.size() > 1) { folly::dynamic newPropsDynamic = folly::dynamic::object; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp index 5fb36a948ce0..1fd23e8ff3c9 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.cpp @@ -88,9 +88,6 @@ ReanimatedModuleProxy::ReanimatedModuleProxy( valueUnpackerCode_)), eventHandlerRegistry_(std::make_unique()), requestRender_(platformDepMethodsHolder.requestRender), - #ifdef RCT_NEW_ARCH_ENABLED - getAnimationTimestamp_(platformDepMethodsHolder.getAnimationTimestamp), - #endif // RCT_NEW_ARCH_ENABLED animatedSensorModule_(platformDepMethodsHolder), jsLogger_( std::make_shared(workletsModuleProxy->getJSScheduler())), @@ -756,22 +753,6 @@ jsi::Value ReanimatedModuleProxy::filterNonAnimatableProps( } return nonAnimatableProps; } - -jsi::Value ReanimatedModuleProxy::getSettledUpdates(jsi::Runtime &rt) { - // TODO(future): use unified timestamp - const auto currentTimestamp = getAnimationTimestamp_(); - - const auto lock = propsRegistry_->createLock(); - - // TODO: fix bug when threshold difference is smaller than 1 second - // TODO(future): flush updates from CSS animations and CSS transitions registries - propsRegistry_->removeUpdatesOlderThanTimestamp(currentTimestamp - 2000); // 2 seconds - - // TODO(future): find a better way to obtain timestamp for removing updates - // TODO(future): move removing old updates to separate method - - return propsRegistry_->getUpdatesOlderThanTimestamp(rt, currentTimestamp - 1000); // 1 second -} #endif // RCT_NEW_ARCH_ENABLED bool ReanimatedModuleProxy::handleEvent( @@ -886,9 +867,6 @@ void ReanimatedModuleProxy::performOperations(const bool isTriggeredByEvent, con propsRegistry_->shouldReanimatedSkipCommit()) { propsRegistry_->pleaseCommitAfterPause(); } - #ifdef RCT_NEW_ARCH_ENABLED - const auto currentTimestamp = this->getAnimationTimestamp_(); - #endif // RCT_NEW_ARCH_ENABLED // Even if only non-layout props are changed, we need to store the update // in PropsRegistry anyway so that React doesn't overwrite it in the next @@ -906,12 +884,7 @@ void ReanimatedModuleProxy::performOperations(const bool isTriggeredByEvent, con // Still need to convert to dynamic for propsRegistry folly::dynamic propsDynamic = dynamicFromValue(rt, *props); - - #ifdef RCT_NEW_ARCH_ENABLED - propsRegistry_->update(shadowNode, std::move(propsDynamic), currentTimestamp); - #else propsRegistry_->update(shadowNode, std::move(propsDynamic)); - #endif // RCT_NEW_ARCH_ENABLED } } diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h index 794236f2221b..3e6881a96f67 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxy.h @@ -203,14 +203,6 @@ class ReanimatedModuleProxy return workletsModuleProxy_; } -#ifdef RCT_NEW_ARCH_ENABLED - #ifdef ANDROID - jsi::Value getSettledUpdates(jsi::Runtime &rt) override; - #else // IOS - jsi::Value getSettledUpdates(jsi::Runtime &rt); - #endif // ANDROID -#endif // RCT_NEW_ARCH_ENABLED - private: void requestAnimationFrame(jsi::Runtime &rt, const jsi::Value &callback); @@ -229,7 +221,6 @@ class ReanimatedModuleProxy std::unique_ptr eventHandlerRegistry_; const RequestRenderFunction requestRender_; - const GetAnimationTimestampFunction getAnimationTimestamp_; std::vector> frameCallbacks_; volatile bool renderRequested_{false}; std::function onRenderCallback_; diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp index 0c738db977c8..f160f495917d 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.cpp @@ -192,11 +192,6 @@ static jsi::Value REANIMATED_SPEC_PREFIX(setNodeRemovalCallback)( return jsi::Value::undefined(); } -static jsi::Value REANIMATED_SPEC_PREFIX( - getSettledUpdates)(jsi::Runtime &rt, TurboModule &turboModule, const jsi::Value *args, size_t) { - return static_cast(&turboModule)->getSettledUpdates(rt); -} - #endif // RCT_NEW_ARCH_ENABLED ReanimatedModuleProxySpec::ReanimatedModuleProxySpec( @@ -243,8 +238,6 @@ ReanimatedModuleProxySpec::ReanimatedModuleProxySpec( MethodMetadata{1, REANIMATED_SPEC_PREFIX(unmarkNodeAsRemovable)}; methodMap_["setNodeRemovalCallback"] = MethodMetadata{1, REANIMATED_SPEC_PREFIX(setNodeRemovalCallback)}; - methodMap_["getSettledUpdates"] = - MethodMetadata{1, REANIMATED_SPEC_PREFIX(getSettledUpdates)}; #endif // RCT_NEW_ARCH_ENABLED } } // namespace reanimated diff --git a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h index def6114e53f0..975fc2503a96 100644 --- a/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h +++ b/packages/react-native-reanimated/Common/cpp/reanimated/NativeModules/ReanimatedModuleProxySpec.h @@ -106,7 +106,6 @@ class JSI_EXPORT ReanimatedModuleProxySpec : public TurboModule { virtual void setNodeRemovalCallback( jsi::Runtime &rt, const jsi::Value &callback) = 0; - virtual jsi::Value getSettledUpdates(jsi::Runtime &rt) = 0; #endif // RCT_NEW_ARCH_ENABLED }; diff --git a/packages/react-native-reanimated/src/Colors.ts b/packages/react-native-reanimated/src/Colors.ts index b68aa6c6b380..ebb0604a0d91 100644 --- a/packages/react-native-reanimated/src/Colors.ts +++ b/packages/react-native-reanimated/src/Colors.ts @@ -629,9 +629,7 @@ export const hsvToColor = ( return rgbaColor(r, g, b, a); }; -export function processColorInitially( - color: unknown -): number | null | undefined { +function processColorInitially(color: unknown): number | null | undefined { 'worklet'; if (color === null || color === undefined) { return color; diff --git a/packages/react-native-reanimated/src/PropsRegistryGarbageCollector.ts b/packages/react-native-reanimated/src/PropsRegistryGarbageCollector.ts deleted file mode 100644 index 7b46d5b6245d..000000000000 --- a/packages/react-native-reanimated/src/PropsRegistryGarbageCollector.ts +++ /dev/null @@ -1,79 +0,0 @@ -'use strict'; - -import { - unprocessColor, - unprocessColorsInProps, -} from './common/style/processors/colors'; -import type { StyleProps } from './commonTypes'; -import type { IAnimatedComponentInternal } from './createAnimatedComponent/commonTypes'; -import { ReanimatedModule } from './ReanimatedModule'; - -const FLUSH_INTERVAL_MS = 500; - -export const PropsRegistryGarbageCollector = { - viewsCount: 0, - viewsMap: new Map(), - intervalId: null as NodeJS.Timeout | null, - - registerView(viewTag: number, component: IAnimatedComponentInternal) { - if (this.viewsMap.has(viewTag)) { - // In case of nested AnimatedComponents (like with inside), - // `registerView` method is called first for the inner component (e.g. ) - // and then second time for the outer component (e.g. ). - // Both of these components have the same viewTag so the inner component will be overwritten - // with the outer one. That's why we need to skip the logic during any subsequent calls. - return; - } - this.viewsMap.set(viewTag, component); - this.viewsCount++; - if (this.viewsCount === 1) { - this.registerInterval(); - } - }, - - unregisterView(viewTag: number) { - this.viewsMap.delete(viewTag); - this.viewsCount--; - if (this.viewsCount === 0) { - this.unregisterInterval(); - } - }, - - syncPropsBackToReact() { - const settledUpdates = ReanimatedModule.getSettledUpdates(); - for (const { viewTag, styleProps } of settledUpdates) { - const component = this.viewsMap.get(viewTag); - unprocessProps(styleProps); - component?._syncStylePropsBackToReact(styleProps); - } - }, - - registerInterval() { - this.intervalId = setInterval( - this.syncPropsBackToReact.bind(this), - FLUSH_INTERVAL_MS - ); - }, - - unregisterInterval() { - if (this.intervalId !== null) { - clearInterval(this.intervalId); - this.intervalId = null; - } - }, -}; - -function unprocessProps(props: StyleProps) { - unprocessColorsInProps(props); - unprocessBoxShadow(props); -} - -function unprocessBoxShadow(props: StyleProps) { - if (Array.isArray(props.boxShadow)) { - // @ts-ignore props is readonly - props.boxShadow = props.boxShadow.map((boxShadow) => ({ - ...boxShadow, - color: unprocessColor(boxShadow.color), - })); - } -} diff --git a/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts b/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts index 100f7aa5fed8..8401f722ca05 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/NativeReanimated.ts @@ -5,7 +5,6 @@ import type { IReanimatedModule, IWorkletsModule, LayoutAnimationBatchItem, - SettledUpdate, ShadowNodeWrapper, ShareableRef, Value3D, @@ -200,10 +199,6 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti setNodeRemovalCallback(callback: (tag: number, isFrozen: boolean) => void) { this.#reanimatedModuleProxy.setNodeRemovalCallback(callback); } - - getSettledUpdates(): SettledUpdate[] { - return this.#reanimatedModuleProxy.getSettledUpdates(); - } } class DummyReanimatedModuleProxy implements ReanimatedModuleProxy { @@ -243,8 +238,4 @@ class DummyReanimatedModuleProxy implements ReanimatedModuleProxy { getViewProp() { return null!; } - - getSettledUpdates(): SettledUpdate[] { - return []; - } } diff --git a/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts b/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts index 7a92c752686e..9fda67952471 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/js-reanimated/JSReanimated.ts @@ -2,7 +2,6 @@ import type { IReanimatedModule, IWorkletsModule, - SettledUpdate, ShadowNodeWrapper, ShareableRef, Value3D, @@ -268,12 +267,6 @@ class JSReanimated implements IReanimatedModule { } } - getSettledUpdates(): SettledUpdate[] { - throw new ReanimatedError( - '`getSettledUpdates` is not available in JSReanimated.' - ); - } - detectPlatform() { const userAgent = navigator.userAgent || navigator.vendor || window.opera; if (userAgent === undefined) { diff --git a/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts b/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts index 28088d7d016f..756f9234dee4 100644 --- a/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts +++ b/packages/react-native-reanimated/src/ReanimatedModule/reanimatedModuleProxy.ts @@ -2,7 +2,6 @@ import type { LayoutAnimationBatchItem, - SettledUpdate, ShadowNodeWrapper, ShareableRef, Value3D, @@ -74,6 +73,4 @@ export interface ReanimatedModuleProxy { setNodeRemovalCallback( callback: (tag: number, isFrozen: boolean) => void ): void; - - getSettledUpdates(): SettledUpdate[]; } diff --git a/packages/react-native-reanimated/src/common/constants/platform.ts b/packages/react-native-reanimated/src/common/constants/platform.ts deleted file mode 100644 index 6d1ce4b6dda6..000000000000 --- a/packages/react-native-reanimated/src/common/constants/platform.ts +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; -import { Platform } from 'react-native'; - -function isWindowAvailable() { - // the window object is unavailable when building the server portion of a site that uses SSG - // this function shouldn't be used to conditionally render components - // https://www.joshwcomeau.com/react/the-perils-of-rehydration/ - // @ts-ignore Fallback if `window` is undefined. - return typeof window !== 'undefined'; -} - -export const IS_ANDROID: boolean = /* @__PURE__ */ (() => - Platform.OS === 'android')(); -export const IS_IOS: boolean = /* @__PURE__ */ (() => Platform.OS === 'ios')(); -export const IS_WEB: boolean = Platform.OS === 'web'; -export const IS_JEST: boolean = !!process.env.JEST_WORKER_ID; - -export const IS_WINDOWS: boolean = Platform.OS === 'windows'; - -export const IS_WINDOW_AVAILABLE: boolean = isWindowAvailable(); - -export const SHOULD_BE_USE_WEB = IS_JEST || IS_WEB || IS_WINDOWS; diff --git a/packages/react-native-reanimated/src/common/style/processors/colors.ts b/packages/react-native-reanimated/src/common/style/processors/colors.ts deleted file mode 100644 index e4db5f07e9de..000000000000 --- a/packages/react-native-reanimated/src/common/style/processors/colors.ts +++ /dev/null @@ -1,263 +0,0 @@ -'use strict'; -'worklet'; -import type { - DynamicColorIOS as RNDynamicColorIOS, - OpaqueColorValue, -} from 'react-native'; - -import { ColorProperties, processColorInitially } from '../../../Colors'; -import type { StyleProps } from '../../../commonTypes'; -import { ReanimatedError } from '../../../errors'; -import { IS_ANDROID, IS_IOS } from '../../constants/platform'; -// eslint-disable-next-line import/consistent-type-specifier-style -import { type ValueProcessorContext, ValueProcessorTarget } from '../../types'; - -export type UnknownRecord = Record; - -const isRecord = ( - value: unknown -): value is T => - typeof value === 'object' && value !== null && !Array.isArray(value); - -/** - * Copied from: - * https://github.com/facebook/react-native/blob/v0.81.0/packages/react-native/Libraries/StyleSheet/PlatformColorValueTypes.d.ts - */ -export function PlatformColor(...names: string[]): OpaqueColorValue { - return (IS_IOS - ? { semantic: names } - : // eslint-disable-next-line camelcase - { resource_paths: names }) as unknown as OpaqueColorValue; -} - -type PlatformColorObject = - | { semantic: Array; resource_paths?: never } - | { semantic?: never; resource_paths?: Array }; - -function isPlatformColorObject(value: unknown): value is PlatformColorObject { - return ( - isRecord(value) && - (Array.isArray(value.semantic) || Array.isArray(value.resource_paths)) - ); -} - -/* copied from: - * https://github.com/facebook/react-native/blob/v0.81.0/packages/react-native/Libraries/StyleSheet/PlatformColorValueTypesIOS.d.ts - */ -type DynamicColorIOSTuple = Parameters[0]; - -export function DynamicColorIOS(tuple: DynamicColorIOSTuple): OpaqueColorValue { - return { - dynamic: { - light: tuple.light, - dark: tuple.dark, - highContrastLight: tuple.highContrastLight, - highContrastDark: tuple.highContrastDark, - }, - } as unknown as OpaqueColorValue; -} - -type DynamicColorObjectIOS = { - dynamic: DynamicColorIOSTuple; -}; - -function isDynamicColorObjectIOS( - value: unknown -): value is DynamicColorObjectIOS { - return ( - isRecord(value) && - isRecord(value.dynamic) && - 'light' in value.dynamic && - 'dark' in value.dynamic - ); -} - -export const ERROR_MESSAGES = { - invalidColor: (color: unknown) => - `Invalid color value: ${JSON.stringify(color)}`, - invalidProcessedColor: (color: unknown) => - `Invalid processed color value: ${JSON.stringify(color)}`, - dynamicNotAvailableOnPlatform: () => - 'DynamicColorIOS is not available on this platform.', -}; - -export function processColorNumber(value: unknown): number | null { - let normalizedColor = processColorInitially(value); - - // eslint-disable-next-line eqeqeq - if (IS_ANDROID && typeof normalizedColor == 'number') { - // Android use 32 bit *signed* integer to represent the color - // We utilize the fact that bitwise operations in JS also operates on - // signed 32 bit integers, so that we can use those to convert from - // *unsigned* to *signed* 32bit int that way. - normalizedColor = normalizedColor | 0x0; - } - - return normalizedColor as unknown as number | null; -} - -function unprocessColorNumber(value: number): string { - const a = value >>> 24; - const r = (value << 8) >>> 24; - const g = (value << 16) >>> 24; - const b = (value << 24) >>> 24; - return `rgba(${r},${g},${b},${a})`; -} - -export type ProcessedDynamicColorObjectIOS = { - dynamic: { - light: number; - dark: number; - highContrastLight?: number; - highContrastDark?: number; - }; -}; - -const DynamicColorIOSProperties = [ - 'light', - 'dark', - 'highContrastLight', - 'highContrastDark', -] as const; - -function processDynamicColorObjectIOS( - value: DynamicColorObjectIOS -): ProcessedDynamicColorObjectIOS | null { - const result = {} as ProcessedDynamicColorObjectIOS['dynamic']; - - for (const property of DynamicColorIOSProperties) { - if (value.dynamic[property] === undefined) { - continue; - } - - const processed = processColorNumber(value.dynamic[property]); - if (processed === null) { - return null; - } - - result[property] = processed; - } - - return { - dynamic: result, - }; -} - -function unprocessDynamicColorObjectIOS( - value: ProcessedDynamicColorObjectIOS -): DynamicColorObjectIOS { - const result = {} as DynamicColorIOSTuple; - - for (const property of DynamicColorIOSProperties) { - if (value.dynamic[property] !== undefined) { - result[property] = unprocessColorNumber( - value.dynamic[property] as unknown as number - ); - } - } - - return { - dynamic: result, - }; -} - -type ProcessedColor = - | number - | PlatformColorObject - | ProcessedDynamicColorObjectIOS; - -/** - * Processes a color value and returns a normalized color representation. - * - * @param value - The color value to process (string, number, or ColorValue) - * @param context - Optional for target-specific processing context (e.g. CSS) - * @returns The processed color value - `number` for valid colors, `false` for - * transparent colors - */ -export function processColor( - value: string | number, - context?: ValueProcessorContext -): number; -export function processColor( - value: unknown, - context?: ValueProcessorContext -): ProcessedColor; -export function processColor( - value: unknown, - context?: ValueProcessorContext -): ProcessedColor { - let result: ProcessedColor | null = processColorNumber(value); // try to convert to a number first (most common case) - - if (result) { - return result; - } - if (result === 0) { - if ( - context?.target === ValueProcessorTarget.CSS && - value === 'transparent' - ) { - // For CSS, we have to return `false` to distinguish the true 'transparent' from the 0x00000000 color - // and properly interpolate between the transparent and the non-transparent color. - return false as unknown as number; // TODO - figure out a better way to handle this instead of type casting - } - return result; - } - - if (isPlatformColorObject(value)) { - return value; - } - if (isDynamicColorObjectIOS(value)) { - if (!IS_IOS) { - throw new ReanimatedError(ERROR_MESSAGES.dynamicNotAvailableOnPlatform()); - } - result = processDynamicColorObjectIOS(value); - } - - if (result === null) { - throw new ReanimatedError(ERROR_MESSAGES.invalidColor(value)); - } - - return result; -} - -export function unprocessColor( - value: ProcessedColor -): string | PlatformColorObject | DynamicColorObjectIOS { - if (typeof value === 'number') { - return unprocessColorNumber(value); - } - if (isPlatformColorObject(value)) { - return value; - } - if (isDynamicColorObjectIOS(value)) { - if (!IS_IOS) { - throw new ReanimatedError(ERROR_MESSAGES.dynamicNotAvailableOnPlatform()); - } - return unprocessDynamicColorObjectIOS(value); - } - throw new ReanimatedError(ERROR_MESSAGES.invalidProcessedColor(value)); -} - -export function processColorsInProps(props: StyleProps) { - for (const key in props) { - if (!ColorProperties.includes(key)) { - continue; - } - const value = props[key]; - props[key] = Array.isArray(value) - ? value.map((c) => processColor(c)) - : processColor(value); - } -} - -export function unprocessColorsInProps(props: StyleProps) { - for (const key in props) { - if (!ColorProperties.includes(key)) { - continue; - } - const value = props[key]; - props[key] = Array.isArray(value) - ? value.map((c) => unprocessColor(c)) - : unprocessColor(value); - } -} diff --git a/packages/react-native-reanimated/src/common/types/index.ts b/packages/react-native-reanimated/src/common/types/index.ts deleted file mode 100644 index 0a129ca351e8..000000000000 --- a/packages/react-native-reanimated/src/common/types/index.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -'use strict'; - -import type { ComponentType } from 'react'; - -export type Maybe = T | null | undefined; - -/** - * Makes only mutable types (objects, arrays) readonly while leaving primitive - * types unchanged. This prevents type issues caused by making other types - * readonly, like Readonly which isn't the same as string. - */ -export type NonMutable = T extends object ? Readonly : T; - -export type AnyRecord = Record; -export type UnknownRecord = Record; - -export type AnyComponent = ComponentType; - -type Simplify = { - [K in keyof T]: T[K]; - // eslint-disable-next-line @typescript-eslint/ban-types -} & {}; - -type ConvertValueToArray = Simplify<(T extends any[] ? T[number] : T)[]>; - -export type ConvertValuesToArrays = { - [K in keyof T]-?: ConvertValueToArray>; -}; - -export type ConvertValuesToArraysWithUndefined = { - [K in keyof T]-?: ConvertValueToArray; -}; - -export enum ValueProcessorTarget { - CSS = 'css', - Default = 'default', -} - -export type ValueProcessorContext = { - target: ValueProcessorTarget; -}; - -export type ValueProcessor = ( - value: NonMutable, - context?: ValueProcessorContext -) => Maybe | Record; - -export type ConfigPropertyAlias

= { - as: keyof P; -}; diff --git a/packages/react-native-reanimated/src/commonTypes.ts b/packages/react-native-reanimated/src/commonTypes.ts index 8c6a6d65bb32..a28c136b6726 100644 --- a/packages/react-native-reanimated/src/commonTypes.ts +++ b/packages/react-native-reanimated/src/commonTypes.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ 'use strict'; import type { HostInstance, @@ -514,11 +513,6 @@ export type ShadowNodeWrapper = { __nativeStateShadowNodeWrapper: never; }; -export type SettledUpdate = { - viewTag: number; - styleProps: StyleProps; -}; - export enum KeyboardState { UNKNOWN = 0, OPENING = 1, diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts b/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts index c910b98d0a4b..6ad84070ace9 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts +++ b/packages/react-native-reanimated/src/createAnimatedComponent/commonTypes.ts @@ -141,8 +141,6 @@ export interface IAnimatedComponentInternal { _updateReanimatedProps: (props: StyleProps) => void; /** Detach styles from view descriptors */ _detachStyles: () => void; - - _syncStylePropsBackToReact: (props: StyleProps) => void; } export type NestedArray = T | NestedArray[]; diff --git a/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx b/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx index 372a62ba4fa1..23616f031062 100644 --- a/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx +++ b/packages/react-native-reanimated/src/createAnimatedComponent/createAnimatedComponent.tsx @@ -13,7 +13,7 @@ import type { } from 'react'; import React from 'react'; import type { FlatList, FlatListProps } from 'react-native'; -import { Platform, processColor, StyleSheet } from 'react-native'; +import { Platform, processColor } from 'react-native'; import { getReduceMotionFromConfig } from '../animation/util'; import { maybeBuild } from '../animationBuilder'; @@ -50,7 +50,6 @@ import { isWeb, shouldBeUseWeb, } from '../PlatformChecker'; -import { PropsRegistryGarbageCollector } from '../PropsRegistryGarbageCollector'; import { componentWithRef } from '../reactUtils'; import type { ReanimatedHTMLElement } from '../ReanimatedModule/js-reanimated'; import { updateLayoutAnimations } from '../UpdateLayoutAnimations'; @@ -173,7 +172,7 @@ export function createAnimatedComponent( class AnimatedComponent extends React.Component< AnimatedComponentProps, - { reanimatedProps: { [key: string]: unknown }; settledProps: StyleProps } + { reanimatedProps: { [key: string]: unknown } } > implements IAnimatedComponentInternal { @@ -201,13 +200,14 @@ export function createAnimatedComponent( constructor(props: AnimatedComponentProps) { super(props); - if (IS_JEST) { this.jestAnimatedStyle = { value: {} }; this.jestAnimatedProps = { value: {} }; } - this.state = { settledProps: {}, reanimatedProps: {} }; + this.state = { + reanimatedProps: {}, + }; const skipEntering = this.context?.current; if (isFabric() && !skipEntering) { @@ -229,11 +229,6 @@ export function createAnimatedComponent( this._InlinePropManager.attachInlineProps(this, this._getViewInfo()); const viewTag = this.getComponentViewTag(); - - if (isFabric() && viewTag !== -1) { - PropsRegistryGarbageCollector.registerView(viewTag, this); - } - if (viewTag !== -1) { ComponentRegistry.register(viewTag, this); } @@ -304,9 +299,6 @@ export function createAnimatedComponent( this._jsPropsUpdater.removeOnJSPropsChangeListener(this); const viewTag = this.getComponentViewTag(); - if (isFabric() && viewTag !== -1) { - PropsRegistryGarbageCollector.unregisterView(viewTag); - } // Defer cleanup for Fabric (freeze detection via callback), immediate for Paper/Web if (!SHOULD_BE_USE_WEB && isFabric()) { @@ -366,10 +358,6 @@ export function createAnimatedComponent( } } - _syncStylePropsBackToReact(props: StyleProps) { - this.setState({ reanimatedProps: props }); - } - getComponentViewTag() { return this._getViewInfo().viewTag as number; } @@ -770,6 +758,7 @@ export function createAnimatedComponent( render() { const filteredProps = this._PropsFilter.filterNonAnimatedProps(this); + if (IS_JEST) { filteredProps.jestAnimatedStyle = this.jestAnimatedStyle; filteredProps.jestAnimatedProps = this.jestAnimatedProps; @@ -811,29 +800,6 @@ export function createAnimatedComponent( } : {}; - if (isFabric()) { - const flatStyles = StyleSheet.flatten(filteredProps.style as object); - const mergedStyles = { - ...flatStyles, - ...this.state.reanimatedProps, - }; - - return ( - void} - {...platformProps} - /> - ); - } - return (