[Perf] Teact: Fix memory leaks

This commit is contained in:
Alexander Zinchuk 2022-01-21 17:29:48 +01:00
parent 3a282b8086
commit 29852d2bf6
3 changed files with 51 additions and 36 deletions

View File

@ -60,7 +60,11 @@ export default function useScrollHooks(
return; return;
} }
const { offsetHeight, scrollHeight, scrollTop } = containerRef.current!; if (!containerRef.current) {
return;
}
const { offsetHeight, scrollHeight, scrollTop } = containerRef.current;
const scrollBottom = Math.round(scrollHeight - scrollTop - offsetHeight); const scrollBottom = Math.round(scrollHeight - scrollTop - offsetHeight);
const isNearBottom = scrollBottom <= FAB_THRESHOLD; const isNearBottom = scrollBottom <= FAB_THRESHOLD;
const isAtBottom = scrollBottom <= NOTCH_THRESHOLD; const isAtBottom = scrollBottom <= NOTCH_THRESHOLD;

View File

@ -122,14 +122,20 @@ function renderWithVirtual(
unmountTree($current); unmountTree($current);
} else { } else {
const areComponents = isComponentElement($current) && isComponentElement($new); const areComponents = isComponentElement($current) && isComponentElement($new);
const currentTarget = getTarget($current);
if (!areComponents) { if (!areComponents) {
setTarget($new, getTarget($current)!); setTarget($new, currentTarget!);
setTarget($current, undefined as any); // Help GC
if ('props' in $current && 'props' in $new) {
$new.props.ref = $current.props.ref;
}
} }
if (isRealElement($current) && isRealElement($new)) { if (isRealElement($current) && isRealElement($new)) {
if (moveDirection) { if (moveDirection) {
const node = getTarget($current)!; const node = currentTarget!;
const nextSibling = parentEl.childNodes[moveDirection === 'up' ? index : index + 1]; const nextSibling = parentEl.childNodes[moveDirection === 'up' ? index : index + 1];
if (nextSibling) { if (nextSibling) {
@ -140,13 +146,13 @@ function renderWithVirtual(
} }
if (!areComponents) { if (!areComponents) {
updateAttributes($current, $new, getTarget($current) as HTMLElement); updateAttributes($current, $new, currentTarget as HTMLElement);
} }
$new.children = renderChildren( $new.children = renderChildren(
$current, $current,
$new, $new,
areComponents ? parentEl : getTarget($current) as HTMLElement, areComponents ? parentEl : currentTarget as HTMLElement,
); );
} }
} }

View File

@ -336,17 +336,26 @@ export function hasElementChanged($old: VirtualElement, $new: VirtualElement) {
} }
export function unmountTree($element: VirtualElement) { export function unmountTree($element: VirtualElement) {
if (!isRealElement($element)) {
return;
}
if (isComponentElement($element)) { if (isComponentElement($element)) {
unmountComponent($element.componentInstance); unmountComponent($element.componentInstance);
} else if ($element.target) { } else {
removeAllDelegatedListeners($element.target as HTMLElement); if (isTagElement($element)) {
// Trying to help GC if ($element.target) {
// eslint-disable-next-line no-null/no-null removeAllDelegatedListeners($element.target as HTMLElement);
$element.target = null as any; }
if ($element.props.ref) {
$element.props.ref.current = undefined; // Help GC
}
}
if ($element.target) {
$element.target = undefined; // Help GC
}
if (!isRealElement($element)) {
return;
}
} }
$element.children.forEach(unmountTree); $element.children.forEach(unmountTree);
@ -363,9 +372,9 @@ function unmountComponent(componentInstance: ComponentInstance) {
return; return;
} }
componentInstance.hooks.memos.byCursor.forEach((hook) => { // We need to clean refs before running effect cleanups
// eslint-disable-next-line no-null/no-null componentInstance.hooks.memos.byCursor.forEach((memoContainer) => {
hook.current = null; memoContainer.current = undefined;
}); });
componentInstance.hooks.effects.byCursor.forEach(({ cleanup }) => { componentInstance.hooks.effects.byCursor.forEach(({ cleanup }) => {
@ -383,35 +392,31 @@ function unmountComponent(componentInstance: ComponentInstance) {
helpGc(componentInstance); helpGc(componentInstance);
} }
// We need to remove all references to DOM objects. We also clean all other references, just in case. // We need to remove all references to DOM objects. We also clean all other references, just in case
function helpGc(componentInstance: ComponentInstance) { function helpGc(componentInstance: ComponentInstance) {
/* eslint-disable no-null/no-null */
componentInstance.hooks.effects.byCursor.forEach((hook) => { componentInstance.hooks.effects.byCursor.forEach((hook) => {
hook.cleanup = null as any; hook.cleanup = undefined;
hook.effect = null as any; hook.effect = undefined as any;
hook.dependencies = null as any; hook.dependencies = undefined;
}); });
componentInstance.hooks.state.byCursor.forEach((hook) => { componentInstance.hooks.state.byCursor.forEach((hook) => {
hook.value = null as any; hook.value = undefined;
hook.nextValue = null as any; hook.nextValue = undefined;
hook.setter = null as any; hook.setter = undefined as any;
}); });
componentInstance.hooks.memos.byCursor.forEach((hook) => { componentInstance.hooks.memos.byCursor.forEach((hook) => {
hook.dependencies = null as any; hook.dependencies = undefined as any;
}); });
componentInstance.hooks = null as any; componentInstance.hooks = undefined as any;
componentInstance.$element = null as any; componentInstance.$element = undefined as any;
componentInstance.renderedValue = null as any; componentInstance.renderedValue = undefined;
componentInstance.Component = null as any; componentInstance.Component = undefined as any;
componentInstance.props = null as any; componentInstance.props = undefined as any;
componentInstance.forceUpdate = null as any; componentInstance.forceUpdate = undefined;
componentInstance.onUpdate = null as any; componentInstance.onUpdate = undefined;
/* eslint-enable no-null/no-null */
} }
function prepareComponentForFrame(componentInstance: ComponentInstance) { function prepareComponentForFrame(componentInstance: ComponentInstance) {