[Perf] Teact: Properly GC refs on unmount

This commit is contained in:
Alexander Zinchuk 2022-05-16 13:33:55 +02:00
parent 0f0aba96c8
commit 188251f0fb

View File

@ -76,10 +76,16 @@ interface ComponentInstance {
memos: { memos: {
cursor: number; cursor: number;
byCursor: { byCursor: {
current: any; value: any;
dependencies: any[]; dependencies: any[];
}[]; }[];
}; };
refs: {
cursor: number;
byCursor: {
current: any;
}[];
};
}; };
prepareForFrame?: () => void; prepareForFrame?: () => void;
forceUpdate?: () => void; forceUpdate?: () => void;
@ -176,6 +182,10 @@ function createComponentInstance(Component: FC, props: Props, children: any[]):
cursor: 0, cursor: 0,
byCursor: [], byCursor: [],
}, },
refs: {
cursor: 0,
byCursor: [],
},
}, },
}; };
@ -259,6 +269,7 @@ export function renderComponent(componentInstance: ComponentInstance) {
componentInstance.hooks.state.cursor = 0; componentInstance.hooks.state.cursor = 0;
componentInstance.hooks.effects.cursor = 0; componentInstance.hooks.effects.cursor = 0;
componentInstance.hooks.memos.cursor = 0; componentInstance.hooks.memos.cursor = 0;
componentInstance.hooks.refs.cursor = 0;
const { Component, props } = componentInstance; const { Component, props } = componentInstance;
let newRenderedValue; let newRenderedValue;
@ -373,11 +384,6 @@ function unmountComponent(componentInstance: ComponentInstance) {
return; return;
} }
// We need to clean refs before running effect cleanups
componentInstance.hooks.memos.byCursor.forEach((memoContainer) => {
memoContainer.current = undefined;
});
componentInstance.hooks.effects.byCursor.forEach((effect) => { componentInstance.hooks.effects.byCursor.forEach((effect) => {
if (effect.cleanup) { if (effect.cleanup) {
try { try {
@ -410,9 +416,14 @@ function helpGc(componentInstance: ComponentInstance) {
}); });
componentInstance.hooks.memos.byCursor.forEach((hook) => { componentInstance.hooks.memos.byCursor.forEach((hook) => {
hook.value = undefined as any;
hook.dependencies = undefined as any; hook.dependencies = undefined as any;
}); });
componentInstance.hooks.refs.byCursor.forEach((hook) => {
hook.current = undefined as any;
});
componentInstance.hooks = undefined as any; componentInstance.hooks = undefined as any;
componentInstance.$element = undefined as any; componentInstance.$element = undefined as any;
componentInstance.renderedValue = undefined; componentInstance.renderedValue = undefined;
@ -643,7 +654,7 @@ export function useLayoutEffect(effect: () => Function | void, dependencies?: re
export function useMemo<T extends any>(resolver: () => T, dependencies: any[], debugKey?: string): T { export function useMemo<T extends any>(resolver: () => T, dependencies: any[], debugKey?: string): T {
const { cursor, byCursor } = renderingInstance.hooks.memos; const { cursor, byCursor } = renderingInstance.hooks.memos;
let { current } = byCursor[cursor] || {}; let { value } = byCursor[cursor] || {};
if ( if (
byCursor[cursor] === undefined byCursor[cursor] === undefined
@ -659,17 +670,17 @@ export function useMemo<T extends any>(resolver: () => T, dependencies: any[], d
); );
} }
current = resolver(); value = resolver();
} }
byCursor[cursor] = { byCursor[cursor] = {
current, value,
dependencies, dependencies,
}; };
renderingInstance.hooks.memos.cursor++; renderingInstance.hooks.memos.cursor++;
return current; return value;
} }
export function useCallback<F extends AnyFunction>(newCallback: F, dependencies: any[], debugKey?: string): F { export function useCallback<F extends AnyFunction>(newCallback: F, dependencies: any[], debugKey?: string): F {
@ -682,10 +693,16 @@ export function useRef<T>(): { current: T | undefined }; // TT way (empty is `un
export function useRef<T>(initial: null): { current: T | null }; // React way (empty is `null`) export function useRef<T>(initial: null): { current: T | null }; // React way (empty is `null`)
// eslint-disable-next-line no-null/no-null // eslint-disable-next-line no-null/no-null
export function useRef<T>(initial?: T | null) { export function useRef<T>(initial?: T | null) {
return useMemo(() => ({ const { cursor, byCursor } = renderingInstance.hooks.refs;
current: initial, if (!byCursor[cursor]) {
// eslint-disable-next-line react-hooks/exhaustive-deps byCursor[cursor] = {
}), []); current: initial,
};
}
renderingInstance.hooks.refs.cursor++;
return byCursor[cursor];
} }
export function memo<T extends FC>(Component: T, debugKey?: string) { export function memo<T extends FC>(Component: T, debugKey?: string) {