Skip to content
Open
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 @@ -87,4 +87,10 @@ void AnimatedPropsRegistry::removeTag(const Tag tag) {
timestampMap_.erase(tag);
}

void AnimatedPropsRegistry::removeAll() {
react_native_assert(UpdatesRegistryManager::isLockedByCurrentThread());
Comment thread
tomekzaw marked this conversation as resolved.
updatesRegistry_.clear();
timestampMap_.clear();
}

} // namespace reanimated
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ class AnimatedPropsRegistry : public UpdatesRegistry {
/// Also removes updates older than `cleanupTimestamp` from the registry.
jsi::Value getUpdatesOlderThanTimestamp(jsi::Runtime &rt, double timestamp, double cleanupTimestamp);

/// Drops every entry. Called by the JS-side GC once the last animated view
/// unmount.
void removeAll();

private:
std::unordered_map<Tag, double> timestampMap_;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -663,6 +663,11 @@ jsi::Value ReanimatedModuleProxy::getSettledUpdates(jsi::Runtime &rt) {
rt, currentTimestamp - 1000 /* 1 second */, currentTimestamp - 2000 /* 2 seconds */);
}

void ReanimatedModuleProxy::removeOrphanedProps(jsi::Runtime &rt) {
auto lock = updatesRegistryManager_->lock();
animatedPropsRegistry_->removeAll();
}

bool ReanimatedModuleProxy::handleEvent(
const std::string &eventName,
const int emitterReactTag,
Expand Down Expand Up @@ -1579,6 +1584,14 @@ jsi::Object ReanimatedModuleProxy::toOptimizedObject(jsi::Runtime &rt) {
return strongThis->getSettledUpdates(rt);
});

addMethod<0>(rt, obj, "removeOrphanedProps", [weakThis = weak_from_this()](jsi::Runtime &rt, const jsi::Value &) {
auto strongThis = weakThis.lock();
if (!strongThis) {
return;
}
strongThis->removeOrphanedProps(rt);
});

addMethod<2>(
rt,
obj,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,8 @@ class ReanimatedModuleProxy : public std::enable_shared_from_this<ReanimatedModu

jsi::Value getSettledUpdates(jsi::Runtime &rt);

void removeOrphanedProps(jsi::Runtime &rt);

void dispatchCommand(
jsi::Runtime &rt,
const jsi::Value &shadowNodeValue,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
'use strict';

import { scheduleOnRN, scheduleOnUI } from 'react-native-worklets';

import {
unprocessColor,
unprocessColorsInProps,
Expand All @@ -11,7 +13,6 @@ import { ReanimatedModule } from './ReanimatedModule';
const FLUSH_INTERVAL_MS = 500;

export const PropsRegistryGarbageCollector = {
viewsCount: 0,
viewsMap: new Map<number, IAnimatedComponentInternal>(),
intervalId: null as NodeJS.Timeout | null,

Expand All @@ -25,17 +26,20 @@ export const PropsRegistryGarbageCollector = {
return;
}
this.viewsMap.set(viewTag, component);
this.viewsCount++;
if (this.viewsCount === 1) {
if (this.viewsMap.size === 1) {
this.registerInterval();
}
},

unregisterView(viewTag: number) {
this.viewsMap.delete(viewTag);
this.viewsCount--;
if (this.viewsCount === 0) {
// `delete` returns false when the tag wasn't tracked (the nested-component
// case registerView skipped above); bail to keep the count symmetric.
if (!this.viewsMap.delete(viewTag)) {
return;
}
if (this.viewsMap.size === 0) {
this.unregisterInterval();
scheduleOrphanedPropsCleanup();
}
},

Expand Down Expand Up @@ -66,6 +70,25 @@ export const PropsRegistryGarbageCollector = {
},
};

// The last animated view just unmounted, so the GC interval has stopped and no
// commit will drain the registry. An in-flight animation frame can still re-add
// the view's props; wait one UI frame for it to run, then clear the registry.
function scheduleOrphanedPropsCleanup() {
scheduleOnUI(() => {
'worklet';
requestAnimationFrame(() => {
'worklet';
scheduleOnRN(removeOrphanedProps);
Comment on lines +77 to +81

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks a bit hacky (scheduleOnUI -> requestAnimationFrame -> scheduleOnRN), it might be flaky

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1, what exactly are you trying to achieve here? Maybe you can just do setTimeout with some heuristic delay?

});
});
}

function removeOrphanedProps() {
if (PropsRegistryGarbageCollector.viewsMap.size === 0) {
ReanimatedModule.removeOrphanedProps();
}
}

function unprocessProps(props: StyleProps) {
unprocessColorsInProps(props);
unprocessBoxShadow(props);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,10 @@ See https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooti
return this.#reanimatedModuleProxy.getSettledUpdates();
}

removeOrphanedProps() {
this.#reanimatedModuleProxy.removeOrphanedProps();
}

registerPseudoStyles(
shadowNodeWrapper: ShadowNodeWrapper,
config: CSSPseudoStyleConfig
Expand Down Expand Up @@ -297,6 +301,8 @@ class DummyReanimatedModuleProxy implements ReanimatedModuleProxy {
return [];
}

removeOrphanedProps(): void {}

registerPseudoStyles(): void {}
unregisterPseudoStyles(): void {}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,12 @@ class JSReanimated implements IReanimatedModule {
);
}

removeOrphanedProps(): void {
throw new Error(
'[Reanimated] `removeOrphanedProps` is not available in JSReanimated.'
);
}

registerPseudoStyles(): void {
throw new Error(
'[Reanimated] `registerPseudoStyles` is not available in JSReanimated.'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ export interface ReanimatedModuleProxy {

getSettledUpdates(): SettledUpdate[];

removeOrphanedProps(): void;

registerPseudoStyles(
shadowNodeWrapper: ShadowNodeWrapper,
config: CSSPseudoStyleConfig
Expand Down