Teact: Store last rendered namespace for components (#6570)

This commit is contained in:
zubiden 2026-01-13 01:14:11 +01:00 committed by Alexander Zinchuk
parent d14da19bd8
commit 4819cf02b3
4 changed files with 67 additions and 16 deletions

View File

@ -24,7 +24,7 @@ import usePrevious from '../hooks/usePrevious';
import { useSignalEffect } from '../hooks/useSignalEffect'; import { useSignalEffect } from '../hooks/useSignalEffect';
import { getIsInBackground } from '../hooks/window/useBackgroundMode'; import { getIsInBackground } from '../hooks/window/useBackgroundMode';
// import Test from './test/TestLocale'; // import Test from './test/TestSvg';
import Auth from './auth/Auth'; import Auth from './auth/Auth';
import Notifications from './common/Notifications'; import Notifications from './common/Notifications';
import UiLoader from './common/UiLoader'; import UiLoader from './common/UiLoader';

View File

@ -1,5 +1,7 @@
import { useState } from '../../lib/teact/teact'; import { useState } from '../../lib/teact/teact';
import useInterval from '../../hooks/schedulers/useInterval';
export function App() { export function App() {
const [stateValue, setStateValue] = useState(false); const [stateValue, setStateValue] = useState(false);
@ -43,9 +45,33 @@ export function App() {
stroke-width="10" stroke-width="10"
fill="none" fill="none"
/> />
<NestedSvg />
<>
<rect x="0" y="0" width="50" height="50" fill="blue" />
<rect x="50" y="0" width="50" height="50" fill={stateValue ? 'red' : 'green'} />
</>
</svg> </svg>
</div> </div>
); );
} }
function NestedSvg() {
const [stateValue, setStateValue] = useState(false);
useInterval(() => {
setStateValue((current) => !current);
}, 1000);
return (
<circle
cx="60"
cy="60"
r="10"
fill={stateValue ? 'red' : 'blue'}
/>
);
}
export default App; export default App;

View File

@ -84,6 +84,14 @@ function render($element: VirtualElement | undefined, parentEl: HTMLElement) {
return undefined; return undefined;
} }
interface RenderWithVirtualOptions {
skipComponentUpdate?: boolean;
nextSibling?: ChildNode;
forceMoveToEnd?: boolean;
fragment?: DocumentFragment;
namespace?: string;
}
function renderWithVirtual<T extends VirtualElement | undefined>( function renderWithVirtual<T extends VirtualElement | undefined>(
parentEl: DOMElement, parentEl: DOMElement,
$current: VirtualElement | undefined, $current: VirtualElement | undefined,
@ -91,13 +99,7 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
$parent: VirtualElementParent | VirtualDomHead, $parent: VirtualElementParent | VirtualDomHead,
currentContext: CurrentContext, currentContext: CurrentContext,
index: number, index: number,
options: { options: RenderWithVirtualOptions = {},
skipComponentUpdate?: boolean;
nextSibling?: ChildNode;
forceMoveToEnd?: boolean;
fragment?: DocumentFragment;
namespace?: string;
} = {},
): T { ): T {
const { skipComponentUpdate, fragment } = options; const { skipComponentUpdate, fragment } = options;
let { nextSibling, namespace } = options; let { nextSibling, namespace } = options;
@ -114,6 +116,10 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
if ($new.props.xmlns) namespace = $new.props.xmlns; if ($new.props.xmlns) namespace = $new.props.xmlns;
} }
if ($new?.type === VirtualType.Component) {
$new.componentInstance.lastMountNamespace = namespace;
}
if ( if (
!skipComponentUpdate !skipComponentUpdate
&& isCurrentComponent && isNewComponent && isCurrentComponent && isNewComponent
@ -215,6 +221,7 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
parentEl, parentEl,
nextSibling, nextSibling,
options.forceMoveToEnd, options.forceMoveToEnd,
namespace,
); );
} else { } else {
const $currentAsReal = $current as VirtualElementReal; const $currentAsReal = $current as VirtualElementReal;
@ -288,7 +295,10 @@ function setupComponentUpdateListener(
$parent, $parent,
currentContext, currentContext,
index, index,
{ skipComponentUpdate: true }, {
skipComponentUpdate: true,
namespace: componentInstance.lastMountNamespace,
},
); );
}; };
} }
@ -461,7 +471,7 @@ function renderChildren(
namespace?: string, namespace?: string,
) { ) {
if (('props' in $new) && $new.props.teactFastList) { if (('props' in $new) && $new.props.teactFastList) {
renderFastListChildren($current, $new, currentContext, currentEl); renderFastListChildren($current, $new, currentContext, currentEl, namespace);
return; 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. // 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. // It also allows to selectively move particular preserved nodes within their DOM list.
function renderFastListChildren( 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 currentChildren = $current.children;
const newChildren = $new.children; const newChildren = $new.children;
@ -586,7 +600,7 @@ function renderFastListChildren(
// This prepends new children to the top // This prepends new children to the top
if (fragmentSize) { if (fragmentSize) {
renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext); renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext, namespace);
fragmentSize = undefined; fragmentSize = undefined;
fragmentIndex = undefined; fragmentIndex = undefined;
} }
@ -604,7 +618,14 @@ function renderFastListChildren(
} }
const nextSibling = currentEl.childNodes[isMovingDown ? i + 1 : i]; 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( const $renderedChild = renderWithVirtual(
currentEl, currentChildInfo.$element, $newChild, $new, currentContext, i, options, currentEl, currentChildInfo.$element, $newChild, $new, currentContext, i, options,
@ -616,7 +637,7 @@ function renderFastListChildren(
// This appends new children to the bottom // This appends new children to the bottom
if (fragmentSize) { if (fragmentSize) {
renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext); renderFragment(fragmentIndex!, fragmentSize, currentEl, $new, currentContext, namespace);
} }
} }
@ -626,13 +647,14 @@ function renderFragment(
parentEl: DOMElement, parentEl: DOMElement,
$parent: VirtualElementParent, $parent: VirtualElementParent,
currentContext: CurrentContext, currentContext: CurrentContext,
namespace?: string,
) { ) {
const nextSibling = parentEl.childNodes[fragmentIndex]; const nextSibling = parentEl.childNodes[fragmentIndex];
if (fragmentSize === 1) { if (fragmentSize === 1) {
const $child = $parent.children[fragmentIndex]; const $child = $parent.children[fragmentIndex];
const $renderedChild = renderWithVirtual( const $renderedChild = renderWithVirtual(
parentEl, undefined, $child, $parent, currentContext, fragmentIndex, { nextSibling }, parentEl, undefined, $child, $parent, currentContext, fragmentIndex, { nextSibling, namespace },
); );
if ($renderedChild !== $child) { if ($renderedChild !== $child) {
$parent.children[fragmentIndex] = $renderedChild; $parent.children[fragmentIndex] = $renderedChild;
@ -645,7 +667,9 @@ function renderFragment(
for (let i = fragmentIndex; i < fragmentIndex + fragmentSize; i++) { for (let i = fragmentIndex; i < fragmentIndex + fragmentSize; i++) {
const $child = $parent.children[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) { if ($renderedChild !== $child) {
$parent.children[i] = $renderedChild; $parent.children[i] = $renderedChild;
} }

View File

@ -80,6 +80,7 @@ interface ComponentInstance {
name: string; name: string;
props: Props; props: Props;
renderedValue?: any; renderedValue?: any;
lastMountNamespace?: string;
mountState: MountState; mountState: MountState;
context?: Record<string, Signal<unknown>>; context?: Record<string, Signal<unknown>>;
hooks?: { hooks?: {