Update Teact from MyTonWallet
This commit is contained in:
parent
c59c6f02b5
commit
6054536ee9
@ -12,7 +12,6 @@ import type {
|
||||
} from './teact';
|
||||
|
||||
import { DEBUG } from '../../config';
|
||||
import { unique } from '../../util/iteratees';
|
||||
import { addEventListener, removeAllDelegatedListeners, removeEventListener } from './dom-events';
|
||||
import {
|
||||
captureImmediateEffects,
|
||||
@ -59,6 +58,8 @@ const headsByElement = new WeakMap<Element, VirtualDomHead>();
|
||||
const extraClasses = new WeakMap<Element, Set<string>>();
|
||||
const extraStyles = new WeakMap<Element, Record<string, string>>();
|
||||
|
||||
const uniqueChildKeysCache = new WeakMap<VirtualElementChildren, (keyof any)[]>();
|
||||
|
||||
let DEBUG_virtualTreeSize = 1;
|
||||
|
||||
function render($element: VirtualElement | undefined, parentEl: HTMLElement) {
|
||||
@ -459,10 +460,6 @@ function renderChildren(
|
||||
forceMoveToEnd = false,
|
||||
namespace?: string,
|
||||
) {
|
||||
if (DEBUG) {
|
||||
DEBUG_checkKeyUniqueness($new.children);
|
||||
}
|
||||
|
||||
if (('props' in $new) && $new.props.teactFastList) {
|
||||
renderFastListChildren($current, $new, currentContext, currentEl);
|
||||
return;
|
||||
@ -532,52 +529,31 @@ function renderFastListChildren(
|
||||
const currentChildren = $current.children;
|
||||
const newChildren = $new.children;
|
||||
|
||||
const newKeys = new Set();
|
||||
for (const $newChild of newChildren) {
|
||||
const key = 'props' in $newChild ? $newChild.props.key : undefined;
|
||||
|
||||
if (DEBUG && isParentElement($newChild)) {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
if (key === undefined || key === null) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('Missing `key` in `teactFastList`');
|
||||
}
|
||||
// Clear out duplicated keys to avoid incorrect elements matching
|
||||
const currentKeysByIndex = getChildKeysByIndex(currentChildren);
|
||||
const newKeysByIndex = getChildKeysByIndex(newChildren);
|
||||
const newKeys = new Set(newKeysByIndex);
|
||||
|
||||
if (DEBUG) {
|
||||
for (const $newChild of newChildren) {
|
||||
if ($newChild.type === VirtualType.Fragment) {
|
||||
throw new Error('[Teact] Fragment can not be child of container with `teactFastList`');
|
||||
}
|
||||
}
|
||||
|
||||
newKeys.add(key);
|
||||
}
|
||||
|
||||
// Build a collection of old children that also remain in the new list
|
||||
let currentRemainingIndex = 0;
|
||||
const remainingByKey: Record<string, { $element: VirtualElement; index: number; orderKey?: number }> = {};
|
||||
const remainingByKey: Record<keyof any, { $element: VirtualElement; index: number; orderKey?: number }> = {};
|
||||
for (let i = 0, l = currentChildren.length; i < l; i++) {
|
||||
const $currentChild = currentChildren[i];
|
||||
|
||||
let key = 'props' in $currentChild ? $currentChild.props.key : undefined;
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
const isKeyPresent = key !== undefined && key !== null;
|
||||
const key = currentKeysByIndex[i];
|
||||
|
||||
// First we process removed children
|
||||
if (isKeyPresent && !newKeys.has(key)) {
|
||||
if (!newKeys.has(key)) {
|
||||
renderWithVirtual(currentEl, $currentChild, undefined, $new, currentContext, -1);
|
||||
|
||||
continue;
|
||||
} else if (!isKeyPresent) {
|
||||
const $newChild = newChildren[i];
|
||||
const newChildKey = ($newChild && 'props' in $newChild) ? $newChild.props.key : undefined;
|
||||
// If a non-key element remains at the same index we preserve it with a virtual `key`
|
||||
if ($newChild && !newChildKey) {
|
||||
key = `${INDEX_KEY_PREFIX}${i}`;
|
||||
// Otherwise, we just remove it
|
||||
} else {
|
||||
renderWithVirtual(currentEl, $currentChild, undefined, $new, currentContext, -1);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Then we build up info about remaining children
|
||||
@ -595,7 +571,7 @@ function renderFastListChildren(
|
||||
|
||||
for (let i = 0, l = newChildren.length; i < l; i++) {
|
||||
const $newChild = newChildren[i];
|
||||
const key = 'props' in $newChild ? $newChild.props.key : `${INDEX_KEY_PREFIX}${i}`;
|
||||
const key = newKeysByIndex[i];
|
||||
const currentChildInfo = remainingByKey[key];
|
||||
|
||||
if (!currentChildInfo) {
|
||||
@ -932,23 +908,57 @@ function DEBUG_addToVirtualTreeSize($current: VirtualElementParent | VirtualDomH
|
||||
});
|
||||
}
|
||||
|
||||
function DEBUG_checkKeyUniqueness(children: VirtualElementChildren) {
|
||||
const firstChild = children[0];
|
||||
if (firstChild && 'props' in firstChild && firstChild.props.key !== undefined) {
|
||||
const keys = children.reduce((acc: any[], child) => {
|
||||
if ('props' in child && child.props.key) {
|
||||
acc.push(child.props.key);
|
||||
/** Returns unique and not missing key for each child */
|
||||
function getChildKeysByIndex(children: VirtualElementChildren) {
|
||||
// The caching makes sense, because each children list is handled at least twice (as the new and the current children)
|
||||
let uniqueKeysByIndex = uniqueChildKeysCache.get(children);
|
||||
if (uniqueKeysByIndex) return uniqueKeysByIndex;
|
||||
|
||||
const seenKeys = new Set<any>();
|
||||
const DEBUG_duplicatedKeys = new Set<any>();
|
||||
|
||||
uniqueKeysByIndex = children.map(($child, index) => {
|
||||
let key = getElementKey($child);
|
||||
|
||||
if (isNullable(key)) {
|
||||
if (DEBUG && isParentElement($child)) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('Missing `key` in `teactFastList`');
|
||||
}
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
key = `${INDEX_KEY_PREFIX}${index}`;
|
||||
} else {
|
||||
if (seenKeys.has(key)) {
|
||||
if (DEBUG) {
|
||||
DEBUG_duplicatedKeys.add(key);
|
||||
}
|
||||
|
||||
if (keys.length !== unique(keys).length) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[Teact] Duplicated keys:', keys.filter((e, i, a) => a.indexOf(e) !== i), children);
|
||||
throw new Error('[Teact] Children keys are not unique');
|
||||
key = `${INDEX_KEY_PREFIX}${index}`;
|
||||
} else {
|
||||
seenKeys.add(key);
|
||||
}
|
||||
}
|
||||
|
||||
return key;
|
||||
});
|
||||
|
||||
if (DEBUG && DEBUG_duplicatedKeys.size) {
|
||||
// eslint-disable-next-line no-console
|
||||
console.warn('[Teact] Duplicated keys:', [...DEBUG_duplicatedKeys], children);
|
||||
throw new Error('[Teact] Children keys are not unique');
|
||||
}
|
||||
|
||||
uniqueChildKeysCache.set(children, uniqueKeysByIndex);
|
||||
return uniqueKeysByIndex;
|
||||
}
|
||||
|
||||
function getElementKey($element: VirtualElement) {
|
||||
return 'props' in $element ? $element.props.key : undefined;
|
||||
}
|
||||
|
||||
function isNullable(value: unknown): value is undefined | null {
|
||||
// eslint-disable-next-line no-null/no-null
|
||||
return value === undefined || value === null;
|
||||
}
|
||||
|
||||
const TeactDOM = { render };
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
/* eslint-disable react-x/no-unnecessary-use-prefix */
|
||||
import type { ReactElement } from 'react';
|
||||
|
||||
import { DEBUG, DEBUG_MORE } from '../../config';
|
||||
@ -137,6 +136,7 @@ export type TeactNode =
|
||||
| string
|
||||
| number
|
||||
| boolean
|
||||
| undefined
|
||||
| TeactNode[];
|
||||
|
||||
type Effect = () => (NoneToVoidFunction | void);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user