From 4819cf02b33416fe5e1376f430c3b9622f034ccb Mon Sep 17 00:00:00 2001
From: zubiden <19638254+zubiden@users.noreply.github.com>
Date: Tue, 13 Jan 2026 01:14:11 +0100
Subject: [PATCH] Teact: Store last rendered namespace for components (#6570)
---
src/components/App.tsx | 2 +-
src/components/test/TestSvg.tsx | 26 ++++++++++++++++
src/lib/teact/teact-dom.ts | 54 ++++++++++++++++++++++++---------
src/lib/teact/teact.ts | 1 +
4 files changed, 67 insertions(+), 16 deletions(-)
diff --git a/src/components/App.tsx b/src/components/App.tsx
index b72012f7b..8effa288b 100644
--- a/src/components/App.tsx
+++ b/src/components/App.tsx
@@ -24,7 +24,7 @@ import usePrevious from '../hooks/usePrevious';
import { useSignalEffect } from '../hooks/useSignalEffect';
import { getIsInBackground } from '../hooks/window/useBackgroundMode';
-// import Test from './test/TestLocale';
+// import Test from './test/TestSvg';
import Auth from './auth/Auth';
import Notifications from './common/Notifications';
import UiLoader from './common/UiLoader';
diff --git a/src/components/test/TestSvg.tsx b/src/components/test/TestSvg.tsx
index 8f147384b..e61674fe9 100644
--- a/src/components/test/TestSvg.tsx
+++ b/src/components/test/TestSvg.tsx
@@ -1,5 +1,7 @@
import { useState } from '../../lib/teact/teact';
+import useInterval from '../../hooks/schedulers/useInterval';
+
export function App() {
const [stateValue, setStateValue] = useState(false);
@@ -43,9 +45,33 @@ export function App() {
stroke-width="10"
fill="none"
/>
+
+
+
+ <>
+
+
+ >
);
}
+function NestedSvg() {
+ const [stateValue, setStateValue] = useState(false);
+
+ useInterval(() => {
+ setStateValue((current) => !current);
+ }, 1000);
+
+ return (
+
+ );
+}
+
export default App;
diff --git a/src/lib/teact/teact-dom.ts b/src/lib/teact/teact-dom.ts
index c1d971ab1..57bc8c490 100644
--- a/src/lib/teact/teact-dom.ts
+++ b/src/lib/teact/teact-dom.ts
@@ -84,6 +84,14 @@ function render($element: VirtualElement | undefined, parentEl: HTMLElement) {
return undefined;
}
+interface RenderWithVirtualOptions {
+ skipComponentUpdate?: boolean;
+ nextSibling?: ChildNode;
+ forceMoveToEnd?: boolean;
+ fragment?: DocumentFragment;
+ namespace?: string;
+}
+
function renderWithVirtual(
parentEl: DOMElement,
$current: VirtualElement | undefined,
@@ -91,13 +99,7 @@ function renderWithVirtual(
$parent: VirtualElementParent | VirtualDomHead,
currentContext: CurrentContext,
index: number,
- options: {
- skipComponentUpdate?: boolean;
- nextSibling?: ChildNode;
- forceMoveToEnd?: boolean;
- fragment?: DocumentFragment;
- namespace?: string;
- } = {},
+ options: RenderWithVirtualOptions = {},
): T {
const { skipComponentUpdate, fragment } = options;
let { nextSibling, namespace } = options;
@@ -114,6 +116,10 @@ function renderWithVirtual(
if ($new.props.xmlns) namespace = $new.props.xmlns;
}
+ if ($new?.type === VirtualType.Component) {
+ $new.componentInstance.lastMountNamespace = namespace;
+ }
+
if (
!skipComponentUpdate
&& isCurrentComponent && isNewComponent
@@ -215,6 +221,7 @@ function renderWithVirtual(
parentEl,
nextSibling,
options.forceMoveToEnd,
+ namespace,
);
} else {
const $currentAsReal = $current as VirtualElementReal;
@@ -288,7 +295,10 @@ function setupComponentUpdateListener(
$parent,
currentContext,
index,
- { skipComponentUpdate: true },
+ {
+ skipComponentUpdate: true,
+ namespace: componentInstance.lastMountNamespace,
+ },
);
};
}
@@ -461,7 +471,7 @@ function renderChildren(
namespace?: string,
) {
if (('props' in $new) && $new.props.teactFastList) {
- renderFastListChildren($current, $new, currentContext, currentEl);
+ renderFastListChildren($current, $new, currentContext, currentEl, namespace);
return;
}
@@ -524,7 +534,11 @@ function renderChildren(
// This function allows to prepend/append a bunch of new DOM nodes to the top/bottom of preserved ones.
// It also allows to selectively move particular preserved nodes within their DOM list.
function renderFastListChildren(
- $current: VirtualElementParent, $new: VirtualElementParent, currentContext: CurrentContext, currentEl: DOMElement,
+ $current: VirtualElementParent,
+ $new: VirtualElementParent,
+ currentContext: CurrentContext,
+ currentEl: DOMElement,
+ namespace?: string,
) {
const currentChildren = $current.children;
const newChildren = $new.children;
@@ -586,7 +600,7 @@ function renderFastListChildren(
// This prepends new children to the top
if (fragmentSize) {
- renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext);
+ renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext, namespace);
fragmentSize = undefined;
fragmentIndex = undefined;
}
@@ -604,7 +618,14 @@ function renderFastListChildren(
}
const nextSibling = currentEl.childNodes[isMovingDown ? i + 1 : i];
- const options = shouldMoveNode ? (nextSibling ? { nextSibling } : { forceMoveToEnd: true }) : undefined;
+ const options: RenderWithVirtualOptions = { namespace };
+ if (shouldMoveNode) {
+ if (nextSibling) {
+ options.nextSibling = nextSibling;
+ } else {
+ options.forceMoveToEnd = true;
+ }
+ }
const $renderedChild = renderWithVirtual(
currentEl, currentChildInfo.$element, $newChild, $new, currentContext, i, options,
@@ -616,7 +637,7 @@ function renderFastListChildren(
// This appends new children to the bottom
if (fragmentSize) {
- renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext);
+ renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext, namespace);
}
}
@@ -626,13 +647,14 @@ function renderFragment(
parentEl: DOMElement,
$parent: VirtualElementParent,
currentContext: CurrentContext,
+ namespace?: string,
) {
const nextSibling = parentEl.childNodes[fragmentIndex];
if (fragmentSize === 1) {
const $child = $parent.children[fragmentIndex];
const $renderedChild = renderWithVirtual(
- parentEl, undefined, $child, $parent, currentContext, fragmentIndex, { nextSibling },
+ parentEl, undefined, $child, $parent, currentContext, fragmentIndex, { nextSibling, namespace },
);
if ($renderedChild !== $child) {
$parent.children[fragmentIndex] = $renderedChild;
@@ -645,7 +667,9 @@ function renderFragment(
for (let i = fragmentIndex; i < fragmentIndex + fragmentSize; i++) {
const $child = $parent.children[i];
- const $renderedChild = renderWithVirtual(parentEl, undefined, $child, $parent, currentContext, i, { fragment });
+ const $renderedChild = renderWithVirtual(
+ parentEl, undefined, $child, $parent, currentContext, i, { fragment, namespace },
+ );
if ($renderedChild !== $child) {
$parent.children[i] = $renderedChild;
}
diff --git a/src/lib/teact/teact.ts b/src/lib/teact/teact.ts
index 588e2dd11..11a4ee25f 100644
--- a/src/lib/teact/teact.ts
+++ b/src/lib/teact/teact.ts
@@ -80,6 +80,7 @@ interface ComponentInstance {
name: string;
props: Props;
renderedValue?: any;
+ lastMountNamespace?: string;
mountState: MountState;
context?: Record>;
hooks?: {