Teact: Disable setting state on unmounted components
This commit is contained in:
parent
af973d6f20
commit
8cdc1b87be
@ -1,24 +1,25 @@
|
|||||||
import type {
|
import type {
|
||||||
VirtualElement,
|
VirtualElement,
|
||||||
VirtualElementComponent,
|
|
||||||
VirtualElementTag,
|
|
||||||
VirtualElementParent,
|
|
||||||
VirtualElementChildren,
|
VirtualElementChildren,
|
||||||
VirtualElementReal,
|
VirtualElementComponent,
|
||||||
VirtualElementFragment,
|
VirtualElementFragment,
|
||||||
|
VirtualElementParent,
|
||||||
|
VirtualElementReal,
|
||||||
|
VirtualElementTag,
|
||||||
} from './teact';
|
} from './teact';
|
||||||
import {
|
import {
|
||||||
|
captureImmediateEffects,
|
||||||
hasElementChanged,
|
hasElementChanged,
|
||||||
isComponentElement,
|
isComponentElement,
|
||||||
isTagElement,
|
|
||||||
isParentElement,
|
|
||||||
isTextElement,
|
|
||||||
isEmptyElement,
|
isEmptyElement,
|
||||||
|
isFragmentElement,
|
||||||
|
isParentElement,
|
||||||
|
isTagElement,
|
||||||
|
isTextElement,
|
||||||
mountComponent,
|
mountComponent,
|
||||||
|
MountState,
|
||||||
renderComponent,
|
renderComponent,
|
||||||
unmountComponent,
|
unmountComponent,
|
||||||
isFragmentElement,
|
|
||||||
captureImmediateEffects,
|
|
||||||
} from './teact';
|
} from './teact';
|
||||||
import { DEBUG } from '../../config';
|
import { DEBUG } from '../../config';
|
||||||
import { addEventListener, removeAllDelegatedListeners, removeEventListener } from './dom-events';
|
import { addEventListener, removeAllDelegatedListeners, removeEventListener } from './dom-events';
|
||||||
@ -103,7 +104,11 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Parent element may have changed, so we need to update the listener closure.
|
// Parent element may have changed, so we need to update the listener closure.
|
||||||
if (!skipComponentUpdate && isNewComponent && ($new as VirtualElementComponent).componentInstance.isMounted) {
|
if (
|
||||||
|
!skipComponentUpdate
|
||||||
|
&& isNewComponent
|
||||||
|
&& ($new as VirtualElementComponent).componentInstance.mountState === MountState.Mounted
|
||||||
|
) {
|
||||||
setupComponentUpdateListener(parentEl, $new as VirtualElementComponent, $parent, index);
|
setupComponentUpdateListener(parentEl, $new as VirtualElementComponent, $parent, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -211,7 +216,7 @@ function initComponent(
|
|||||||
) {
|
) {
|
||||||
const { componentInstance } = $element;
|
const { componentInstance } = $element;
|
||||||
|
|
||||||
if (!componentInstance.isMounted) {
|
if (componentInstance.mountState === MountState.New) {
|
||||||
$element = mountComponent(componentInstance);
|
$element = mountComponent(componentInstance);
|
||||||
setupComponentUpdateListener(parentEl, $element, $parent, index);
|
setupComponentUpdateListener(parentEl, $element, $parent, index);
|
||||||
|
|
||||||
@ -219,8 +224,6 @@ function initComponent(
|
|||||||
if (isComponentElement($firstChild)) {
|
if (isComponentElement($firstChild)) {
|
||||||
$element.children = [initComponent(parentEl, $firstChild, $element, 0)];
|
$element.children = [initComponent(parentEl, $firstChild, $element, 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
componentInstance.isMounted = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $element;
|
return $element;
|
||||||
|
|||||||
@ -57,6 +57,12 @@ export interface VirtualElementFragment {
|
|||||||
|
|
||||||
export type StateHookSetter<T> = (newValue: ((current: T) => T) | T) => void;
|
export type StateHookSetter<T> = (newValue: ((current: T) => T) | T) => void;
|
||||||
|
|
||||||
|
export enum MountState {
|
||||||
|
New,
|
||||||
|
Mounted,
|
||||||
|
Unmounted,
|
||||||
|
}
|
||||||
|
|
||||||
interface ComponentInstance {
|
interface ComponentInstance {
|
||||||
id: number;
|
id: number;
|
||||||
$element: VirtualElementComponent;
|
$element: VirtualElementComponent;
|
||||||
@ -64,7 +70,7 @@ interface ComponentInstance {
|
|||||||
name: string;
|
name: string;
|
||||||
props: Props;
|
props: Props;
|
||||||
renderedValue?: any;
|
renderedValue?: any;
|
||||||
isMounted: boolean;
|
mountState: MountState;
|
||||||
hooks: {
|
hooks: {
|
||||||
state: {
|
state: {
|
||||||
cursor: number;
|
cursor: number;
|
||||||
@ -201,7 +207,7 @@ function createComponentInstance(Component: FC, props: Props, children: any[]):
|
|||||||
...props,
|
...props,
|
||||||
...(parsedChildren && { children: parsedChildren }),
|
...(parsedChildren && { children: parsedChildren }),
|
||||||
},
|
},
|
||||||
isMounted: false,
|
mountState: MountState.New,
|
||||||
hooks: {
|
hooks: {
|
||||||
state: {
|
state: {
|
||||||
cursor: 0,
|
cursor: 0,
|
||||||
@ -438,7 +444,7 @@ export function renderComponent(componentInstance: ComponentInstance) {
|
|||||||
newRenderedValue = componentInstance.renderedValue;
|
newRenderedValue = componentInstance.renderedValue;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (componentInstance.isMounted && newRenderedValue === componentInstance.renderedValue) {
|
if (componentInstance.mountState === MountState.Mounted && newRenderedValue === componentInstance.renderedValue) {
|
||||||
return componentInstance.$element;
|
return componentInstance.$element;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,12 +478,12 @@ export function hasElementChanged($old: VirtualElement, $new: VirtualElement) {
|
|||||||
|
|
||||||
export function mountComponent(componentInstance: ComponentInstance) {
|
export function mountComponent(componentInstance: ComponentInstance) {
|
||||||
renderComponent(componentInstance);
|
renderComponent(componentInstance);
|
||||||
componentInstance.isMounted = true;
|
componentInstance.mountState = MountState.Mounted;
|
||||||
return componentInstance.$element;
|
return componentInstance.$element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function unmountComponent(componentInstance: ComponentInstance) {
|
export function unmountComponent(componentInstance: ComponentInstance) {
|
||||||
if (!componentInstance.isMounted) {
|
if (componentInstance.mountState !== MountState.Mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -492,7 +498,7 @@ export function unmountComponent(componentInstance: ComponentInstance) {
|
|||||||
effect.releaseSignals?.();
|
effect.releaseSignals?.();
|
||||||
});
|
});
|
||||||
|
|
||||||
componentInstance.isMounted = false;
|
componentInstance.mountState = MountState.Unmounted;
|
||||||
|
|
||||||
helpGc(componentInstance);
|
helpGc(componentInstance);
|
||||||
}
|
}
|
||||||
@ -530,7 +536,7 @@ function helpGc(componentInstance: ComponentInstance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function prepareComponentForFrame(componentInstance: ComponentInstance) {
|
function prepareComponentForFrame(componentInstance: ComponentInstance) {
|
||||||
if (!componentInstance.isMounted) {
|
if (componentInstance.mountState !== MountState.Mounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -540,7 +546,7 @@ function prepareComponentForFrame(componentInstance: ComponentInstance) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function forceUpdateComponent(componentInstance: ComponentInstance) {
|
function forceUpdateComponent(componentInstance: ComponentInstance) {
|
||||||
if (!componentInstance.isMounted || !componentInstance.onUpdate) {
|
if (componentInstance.mountState !== MountState.Mounted || !componentInstance.onUpdate) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -563,6 +569,10 @@ export function useState<T>(initial?: T, debugKey?: string): [T, StateHookSetter
|
|||||||
value: initial,
|
value: initial,
|
||||||
nextValue: initial,
|
nextValue: initial,
|
||||||
setter: ((componentInstance) => (newValue: ((current: T) => T) | T) => {
|
setter: ((componentInstance) => (newValue: ((current: T) => T) | T) => {
|
||||||
|
if (componentInstance.mountState === MountState.Unmounted) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (typeof newValue === 'function') {
|
if (typeof newValue === 'function') {
|
||||||
newValue = (newValue as (current: T) => T)(byCursor[cursor].nextValue);
|
newValue = (newValue as (current: T) => T)(byCursor[cursor].nextValue);
|
||||||
}
|
}
|
||||||
@ -644,7 +654,7 @@ function useEffectBase(
|
|||||||
});
|
});
|
||||||
|
|
||||||
const runEffect = () => safeExec(() => {
|
const runEffect = () => safeExec(() => {
|
||||||
if (!componentInstance.isMounted) {
|
if (componentInstance.mountState === MountState.Unmounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user