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