-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
base: main
Are you sure you want to change the base?
Fix react-freeze #7053
Conversation
9435608
to
d044191
Compare
if (ancestors.empty()) { | ||
tagsToRemove.push_back(family->getTag()); | ||
} |
There was a problem hiding this comment.
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
std::vector<Tag> tagsToRemove; | ||
rootNode = cloneShadowTreeWithNewProps(*rootNode, propsMap, tagsToRemove); | ||
if (!tagsToRemove.empty()) { | ||
updatesRegistryManager_->removeBatch(tagsToRemove); | ||
} |
There was a problem hiding this comment.
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(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool empty(); | |
bool isEmpty(); |
void updateSettings( | ||
Tag viewTag, | ||
const SettingsUpdates &settingsUpdates, | ||
double timestamp); | ||
|
||
void update(double timestamp); | ||
bool empty(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bool empty(); | |
bool isEmpty(); |
#ifdef RCT_NEW_ARCH_ENABLED | ||
const RemoveFromPropsRegistryFunction removeFromPropsRegistry, | ||
#else | ||
#ifndef RCT_NEW_ARCH_ENABLED |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#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; |
There was a problem hiding this comment.
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 ''; |
There was a problem hiding this comment.
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); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
const [droped, setNuked] = useState(false); | |
const [nuked, setNuked] = useState(false); |
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 whatreact-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 orperfromOperations
)CSSAnimationsRegistry
: we remove a record from this registry only when we fail to apply it onto the ShadowTree (in the commit hook orperfromOperations
)CSSTransitionsRegistry
: we remove a record from this registry whencomponentWillUnmount
is called and there is no ongoing transition OR we failed to apply it onto the ShadowTree (in the commit hook orperfromOperations
)StaticPropsRegistry
remains the same as it doesn't store any state that needs to be persisted when freeze is usedOne 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) incompnentDidMount
.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 inperformOperations
, but because of thesunchronouslyUpdateProps
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.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 thecheck for registry leaks
button.