Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix react-freeze #7053

Open
wants to merge 13 commits into
base: main
Choose a base branch
from

Conversation

bartlomiejbloniarz
Copy link
Contributor

Summary

This PR is very annoying. Until now we assumed that when componentWillUnmount is called, the component will never reappear. This is false when using <Suspense> (docs), which is exactly what react-freeze does. To fix the issue I had to move our animation cleanup functions to be called only when we are sure that the component will not reappear. This is how the new approach works for each registry:

  • AnimatedPropsRegistry: we remove a record from this registry only when we fail to apply it onto the ShadowTree (in the commit hook or perfromOperations)
  • CSSAnimationsRegistry: we remove a record from this registry only when we fail to apply it onto the ShadowTree (in the commit hook or perfromOperations)
  • CSSTransitionsRegistry: we remove a record from this registry when componentWillUnmount is called and there is no ongoing transition OR we failed to apply it onto the ShadowTree (in the commit hook or perfromOperations)
  • StaticPropsRegistry remains the same as it doesn't store any state that needs to be persisted when freeze is used

One problem with this approach is that we are running animations on components that are freezed. This is a waste of resources. This will be addressed in a separate PR, but the main idea is to pause animations in componentWillUnmount and resume them (with the new timestamp) in compnentDidMount.

Also currently the leak check will say that AnimatedPropsRegistry leaked if we don't reload it. This is because an animation frame comes after the cleanup from the commit hook. Normally this would be cleaned up in performOperations, but because of the sunchronouslyUpdateProps fast-path it won't. The cleanup will still happen on the next commit hook invocation (i.e. if you hit reload on the leak check). This issue soon will be gone as we are removing the fast path in #7014.

Before After
Screen.Recording.2025-02-19.at.13.57.39.mov
Screen.Recording.2025-02-19.at.13.44.07.mov

Test plan

Go through the app, and then visit the FreezeExample. Use the check for registry leaks button.

Comment on lines +82 to +84
if (ancestors.empty()) {
tagsToRemove.push_back(family->getTag());
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's annotate that empty ancestors means that the shadow node was (hopefully) irreversibly detached

Comment on lines +83 to +87
std::vector<Tag> tagsToRemove;
rootNode = cloneShadowTreeWithNewProps(*rootNode, propsMap, tagsToRemove);
if (!tagsToRemove.empty()) {
updatesRegistryManager_->removeBatch(tagsToRemove);
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't like returning values via parameters but let's let it slide

@@ -20,7 +21,8 @@ class StaticPropsRegistry {
folly::dynamic get(Tag viewTag) const;
bool has(Tag viewTag) const;
void remove(Tag viewTag);

void removeBatch(const std::vector<Tag> &tagsToRemove);
bool empty();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
bool empty();
bool isEmpty();

void updateSettings(
Tag viewTag,
const SettingsUpdates &settingsUpdates,
double timestamp);

void update(double timestamp);
bool empty();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
bool empty();
bool isEmpty();

@@ -42,10 +42,13 @@ class UpdatesRegistry {

void flushUpdates(UpdatesBatch &updatesBatch, bool merge);
void collectProps(PropsMap &propsMap);
virtual void removeBatch(const std::vector<Tag> &tagsToRemove);
bool empty();
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
bool empty();
bool isEmpty();

#ifdef RCT_NEW_ARCH_ENABLED
const RemoveFromPropsRegistryFunction removeFromPropsRegistry,
#else
#ifndef RCT_NEW_ARCH_ENABLED
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
#ifndef RCT_NEW_ARCH_ENABLED
#ifdef RCT_NEW_ARCH_ENABLED
// nothing
#else

#ifdef RCT_NEW_ARCH_ENABLED
const RemoveFromPropsRegistryFunction removeFromPropsRegistry,
#else
#ifndef RCT_NEW_ARCH_ENABLED
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
#ifndef RCT_NEW_ARCH_ENABLED
#ifdef RCT_NEW_ARCH_ENABLED
// nothing
#else

@@ -54,7 +54,6 @@ declare global {
}[]
) => void)
| undefined;
var _removeFromPropsRegistry: (viewTags: number[]) => void | undefined;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Add the leak check thing here.

// eslint-disable-next-line no-underscore-dangle
return (global._registriesLeakCheck as () => string)();
}
return '';
Copy link
Collaborator

Choose a reason for hiding this comment

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

return global?._registriesLeakCheck() ?? '';


export default function App() {
const [droped, setNuked] = useState(false);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
const [droped, setNuked] = useState(false);
const [nuked, setNuked] = useState(false);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants