Teact: Allow reusing JSX from variables (#6686)
This commit is contained in:
parent
be036138c3
commit
32f98bc0a0
36
src/components/test/TestJsxVariableReuse.tsx
Normal file
36
src/components/test/TestJsxVariableReuse.tsx
Normal file
@ -0,0 +1,36 @@
|
||||
import { useState } from '../../lib/teact/teact';
|
||||
|
||||
// 1. Make sure "First line" is rendered even if followed by a component with single text.
|
||||
// 2. Make sure it then can be normally removed (target is preserved).
|
||||
// 3. Make sure "Last line" is also rendered.
|
||||
|
||||
const Counter = () => {
|
||||
const [count, setCount] = useState(0);
|
||||
|
||||
return (
|
||||
<div>
|
||||
<button onClick={() => setCount(count + 1)}>Increment</button>
|
||||
<span>
|
||||
Count:
|
||||
{' '}
|
||||
{count}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
const STATIC_COUNTER = <Counter />;
|
||||
|
||||
export function App() {
|
||||
const [withFirstReuse, setWithFirstReuse] = useState(true);
|
||||
return (
|
||||
<div>
|
||||
{withFirstReuse && STATIC_COUNTER}
|
||||
<Counter />
|
||||
{STATIC_COUNTER}
|
||||
<button onClick={() => setWithFirstReuse((current) => !current)}>Toggle first reuse</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
@ -15,6 +15,7 @@
|
||||
text-decoration: none;
|
||||
|
||||
background-color: transparent;
|
||||
|
||||
transition: opacity 150ms ease;
|
||||
|
||||
&:hover {
|
||||
@ -77,7 +78,6 @@
|
||||
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 -256 1792 1792' version='1.1'%0A%3E%3Cg transform='matrix(1,0,0,-1,7.5932203,1217.0847)' id='g3003'%3E%3Cpath d='m 1671,970 q 0,-40 -28,-68 L 919,178 783,42 Q 755,14 715,14 675,14 647,42 L 511,178 149,540 q -28,28 -28,68 0,40 28,68 l 136,136 q 28,28 68,28 40,0 68,-28 l 294,-295 656,657 q 28,28 68,28 40,0 68,-28 l 136,-136 q 28,-28 28,-68 z' style='fill:white'/%3E%3C/g%3E%3C/svg%3E");
|
||||
background-size: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.lovely-chart--button-label {
|
||||
|
||||
@ -11,6 +11,16 @@
|
||||
-webkit-user-select: none;
|
||||
user-select: none;
|
||||
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
font: 300 13px '-apple-system', 'HelveticaNeue', Helvetica, Arial, sans-serif;
|
||||
color: #222222;
|
||||
text-align: left;
|
||||
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
html.theme-dark & {
|
||||
--background-color: #242F3E;
|
||||
--text-color: #ffffff;
|
||||
@ -22,16 +32,6 @@
|
||||
--tooltip-arrow: #D2D5D7;
|
||||
}
|
||||
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
font: 300 13px '-apple-system', 'HelveticaNeue', Helvetica, Arial, sans-serif;
|
||||
color: #222222;
|
||||
text-align: left;
|
||||
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
|
||||
// &.lovely-chart--state-invisible > * {
|
||||
// display: none;
|
||||
// }
|
||||
|
||||
@ -15,7 +15,9 @@ import { DEBUG } from '../../config';
|
||||
import { addEventListener, removeAllDelegatedListeners, removeEventListener } from './dom-events';
|
||||
import {
|
||||
captureImmediateEffects,
|
||||
cloneElement,
|
||||
hasElementChanged,
|
||||
isElementUsed,
|
||||
isParentElement,
|
||||
mountComponent,
|
||||
MountState,
|
||||
@ -104,6 +106,19 @@ function renderWithVirtual<T extends VirtualElement | undefined>(
|
||||
const { skipComponentUpdate, fragment } = options;
|
||||
let { nextSibling, namespace } = options;
|
||||
|
||||
// Since JSX elements are used as is, clone is needed to allow JSX reuse
|
||||
if ($new && $current !== $new && isElementUsed($new)) {
|
||||
const isSelfUpdate = (
|
||||
$new.type === VirtualType.Component
|
||||
&& $current?.type === VirtualType.Component
|
||||
&& $new.componentInstance === $current.componentInstance
|
||||
);
|
||||
if (!isSelfUpdate) {
|
||||
// eslint-disable-next-line react-x/no-clone-element
|
||||
$new = cloneElement($new) as T;
|
||||
}
|
||||
}
|
||||
|
||||
const isCurrentComponent = $current?.type === VirtualType.Component;
|
||||
const isNewComponent = $new?.type === VirtualType.Component;
|
||||
const $newAsReal = $new as VirtualElementReal;
|
||||
|
||||
@ -170,6 +170,45 @@ export function isParentElement($element: VirtualElement): $element is VirtualEl
|
||||
);
|
||||
}
|
||||
|
||||
function cloneElement($element: VirtualElement): VirtualElement {
|
||||
switch ($element.type) {
|
||||
case VirtualType.Empty:
|
||||
return { type: VirtualType.Empty };
|
||||
case VirtualType.Text:
|
||||
return { type: VirtualType.Text, value: $element.value };
|
||||
case VirtualType.Tag:
|
||||
return {
|
||||
type: VirtualType.Tag,
|
||||
tag: $element.tag,
|
||||
props: $element.props,
|
||||
children: $element.children.map(cloneElement),
|
||||
};
|
||||
case VirtualType.Component: {
|
||||
const { componentInstance } = $element;
|
||||
return createComponentInstance(componentInstance.Component, componentInstance.props, []);
|
||||
}
|
||||
case VirtualType.Fragment:
|
||||
return {
|
||||
type: VirtualType.Fragment,
|
||||
children: $element.children.map(cloneElement),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Check if an element has been used (mounted/rendered) and needs cloning for reuse
|
||||
export function isElementUsed($element: VirtualElement): boolean {
|
||||
switch ($element.type) {
|
||||
case VirtualType.Component:
|
||||
return $element.componentInstance.mountState !== MountState.Unmounted;
|
||||
case VirtualType.Fragment:
|
||||
return $element.placeholderTarget !== undefined || $element.children.some(isElementUsed);
|
||||
case VirtualType.Tag:
|
||||
case VirtualType.Text:
|
||||
case VirtualType.Empty:
|
||||
return $element.target !== undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function createElement(
|
||||
source: string | FC | typeof Fragment,
|
||||
props: Props,
|
||||
@ -1061,7 +1100,8 @@ export function DEBUG_resolveComponentName(Component: FC_withDebug) {
|
||||
|
||||
export default {
|
||||
createElement,
|
||||
cloneElement,
|
||||
Fragment,
|
||||
};
|
||||
|
||||
export { createElement, Fragment };
|
||||
export { createElement, cloneElement, Fragment };
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user