Skip to content

Commit 806e605

Browse files
authored
Merge branch 'main' into feat/prop-attr-directives
2 parents 17fee02 + 16aeb9f commit 806e605

File tree

7 files changed

+130
-23
lines changed

7 files changed

+130
-23
lines changed

compat/src/portals.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ function Portal(props) {
3939
nodeType: 1,
4040
parentNode: container,
4141
childNodes: [],
42+
contains: () => true,
4243
appendChild(child) {
4344
this.childNodes.push(child);
4445
_this._container.appendChild(child);

hooks/src/index.js

Lines changed: 4 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,6 @@ let currentHook = 0;
1515
/** @type {Array<import('./internal').Component>} */
1616
let afterPaintEffects = [];
1717

18-
let EMPTY = [];
19-
2018
// Cast to use internal Options type
2119
const options = /** @type {import('./internal').Options} */ (_options);
2220

@@ -60,8 +58,7 @@ options._render = vnode => {
6058
if (hookItem._nextValue) {
6159
hookItem._value = hookItem._nextValue;
6260
}
63-
hookItem._pendingValue = EMPTY;
64-
hookItem._nextValue = hookItem._pendingArgs = undefined;
61+
hookItem._pendingArgs = hookItem._nextValue = undefined;
6562
});
6663
} else {
6764
hooks._pendingEffects.forEach(invokeCleanup);
@@ -84,11 +81,7 @@ options.diffed = vnode => {
8481
if (hookItem._pendingArgs) {
8582
hookItem._args = hookItem._pendingArgs;
8683
}
87-
if (hookItem._pendingValue !== EMPTY) {
88-
hookItem._value = hookItem._pendingValue;
89-
}
9084
hookItem._pendingArgs = undefined;
91-
hookItem._pendingValue = EMPTY;
9285
});
9386
}
9487
previousComponent = currentComponent = null;
@@ -159,7 +152,7 @@ function getHookState(index, type) {
159152
});
160153

161154
if (index >= hooks._list.length) {
162-
hooks._list.push({ _pendingValue: EMPTY });
155+
hooks._list.push({});
163156
}
164157

165158
return hooks._list[index];
@@ -350,10 +343,9 @@ export function useMemo(factory, args) {
350343
/** @type {import('./internal').MemoHookState<T>} */
351344
const state = getHookState(currentIndex++, 7);
352345
if (argsChanged(state._args, args)) {
353-
state._pendingValue = factory();
354-
state._pendingArgs = args;
346+
state._value = factory();
347+
state._args = args;
355348
state._factory = factory;
356-
return state._pendingValue;
357349
}
358350

359351
return state._value;

hooks/test/browser/combinations.test.js

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -477,4 +477,44 @@ describe('combinations', () => {
477477
'anchor effect'
478478
]);
479479
});
480+
481+
it('should not loop infinitely', () => {
482+
const actions = [];
483+
let toggle;
484+
function App() {
485+
const [value, setValue] = useState(false);
486+
487+
const data = useMemo(() => {
488+
actions.push('memo');
489+
return {};
490+
}, [value]);
491+
492+
const [prevData, setPreviousData] = useState(data);
493+
if (prevData !== data) {
494+
setPreviousData(data);
495+
}
496+
497+
actions.push('render');
498+
toggle = () => setValue(!value);
499+
return <div>Value: {JSON.stringify(value)}</div>;
500+
}
501+
502+
act(() => {
503+
render(<App />, scratch);
504+
});
505+
expect(actions).to.deep.equal(['memo', 'render']);
506+
expect(scratch.innerHTML).to.deep.equal('<div>Value: false</div>');
507+
508+
act(() => {
509+
toggle();
510+
});
511+
expect(actions).to.deep.equal([
512+
'memo',
513+
'render',
514+
'memo',
515+
'render',
516+
'render'
517+
]);
518+
expect(scratch.innerHTML).to.deep.equal('<div>Value: true</div>');
519+
});
480520
});

hooks/test/browser/useMemo.test.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,13 @@ describe('useMemo', () => {
151151
act(() => {
152152
set('bye');
153153
});
154-
expect(calls.length).to.equal(5);
154+
expect(calls.length).to.equal(6);
155155
expect(calls).to.deep.equal([
156156
'doing memo',
157157
'render hi',
158158
'doing memo',
159-
'render bye', // We expect a missing "doing memo" here because we return to the previous args value
159+
'render bye',
160+
'doing memo',
160161
'render hi'
161162
]);
162163
});

mangle.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131
"$_list": "__",
3232
"$_pendingEffects": "__h",
3333
"$_value": "__",
34-
"$_pendingValue": "__V",
3534
"$_nextValue": "__N",
3635
"$_original": "__v",
3736
"$_args": "__H",

src/diff/children.js

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -116,8 +116,12 @@ export function diffChildren(
116116
childVNode._flags & INSERT_VNODE ||
117117
oldVNode._children === childVNode._children
118118
) {
119-
// @ts-expect-error olDom should be present on a DOM node
120-
if (oldDom && !oldDom.isConnected) {
119+
if (
120+
oldDom &&
121+
typeof childVNode.type == 'string' &&
122+
// @ts-expect-error olDom should be present on a DOM node
123+
!parentDom.contains(oldDom)
124+
) {
121125
oldDom = getDomSibling(oldVNode);
122126
}
123127
oldDom = insert(childVNode, oldDom, parentDom);
@@ -302,20 +306,26 @@ function constructNewChildrenArray(newParentVNode, renderResult, oldChildren) {
302306
childVNode._flags |= INSERT_VNODE;
303307
}
304308
} else if (matchingIndex !== skewedIndex) {
305-
if (matchingIndex === skewedIndex + 1) {
309+
if (matchingIndex == skewedIndex - 1) {
310+
skew = matchingIndex - skewedIndex;
311+
} else if (matchingIndex == skewedIndex + 1) {
306312
skew++;
307313
} else if (matchingIndex > skewedIndex) {
314+
// Our matched DOM-node is further in the list of children than
315+
// where it's at now.
316+
317+
// When the remaining old children is bigger than the new-children
318+
// minus our skewed index we know we are dealing with a shrinking list
319+
// we have to increase our skew with the matchedIndex - the skewed index
308320
if (remainingOldChildren > newChildrenLength - skewedIndex) {
309321
skew += matchingIndex - skewedIndex;
310322
} else {
323+
// If we have matched all the children just decrease the skew
311324
skew--;
312325
}
313326
} else if (matchingIndex < skewedIndex) {
314-
if (matchingIndex == skewedIndex - 1) {
315-
skew = matchingIndex - skewedIndex;
316-
}
317-
} else {
318-
skew = 0;
327+
// When our new position is in front of our old position than we increase the skew
328+
skew++;
319329
}
320330

321331
// Move this VNode's DOM if the original index (matchingIndex) doesn't

test/browser/render.test.js

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1636,4 +1636,68 @@ describe('render()', () => {
16361636
'<div><div>One</div><div>Six</div><div>Seven</div></div>'
16371637
);
16381638
});
1639+
1640+
it('handles shuffled child-ordering', function () {
1641+
const App = ({ items }) => (
1642+
<div>
1643+
{items.map(key => (
1644+
<div key={key}>{key}</div>
1645+
))}
1646+
</div>
1647+
);
1648+
const a = ['0', '1', '2', '3', '4', '5', '6'];
1649+
const b = ['1', '3', '5', '2', '6', '4', '0'];
1650+
const c = ['11', '3', '1', '4', '6', '2', '5', '0', '9', '10'];
1651+
render(<App items={a} />, scratch);
1652+
clearLog();
1653+
expect(scratch.innerHTML).to.equal(
1654+
`<div>${a.map(n => `<div>${n}</div>`).join('')}</div>`
1655+
);
1656+
1657+
render(<App items={b} />, scratch);
1658+
expect(scratch.innerHTML).to.equal(
1659+
`<div>${b.map(n => `<div>${n}</div>`).join('')}</div>`
1660+
);
1661+
expect(getLog()).to.deep.equal([
1662+
'<div>0123456.insertBefore(<div>2, <div>6)',
1663+
'<div>0134526.appendChild(<div>4)',
1664+
'<div>0135264.appendChild(<div>0)'
1665+
]);
1666+
clearLog();
1667+
1668+
render(<App items={c} />, scratch);
1669+
expect(scratch.innerHTML).to.equal(
1670+
`<div>${c.map(n => `<div>${n}</div>`).join('')}</div>`
1671+
);
1672+
expect(getLog()).to.deep.equal([
1673+
'<div>.appendChild(#text)',
1674+
'<div>1352640.insertBefore(<div>11, <div>1)',
1675+
'<div>111352640.insertBefore(<div>1, <div>5)',
1676+
'<div>113152640.insertBefore(<div>6, <div>0)',
1677+
'<div>113152460.insertBefore(<div>2, <div>0)',
1678+
'<div>113154620.insertBefore(<div>5, <div>0)',
1679+
'<div>.appendChild(#text)',
1680+
'<div>113146250.appendChild(<div>9)',
1681+
'<div>.appendChild(#text)',
1682+
'<div>1131462509.appendChild(<div>10)'
1683+
]);
1684+
clearLog();
1685+
1686+
render(<App items={a} />, scratch);
1687+
expect(scratch.innerHTML).to.equal(
1688+
`<div>${a.map(n => `<div>${n}</div>`).join('')}</div>`
1689+
);
1690+
expect(getLog()).to.deep.equal([
1691+
'<div>11.remove()',
1692+
'<div>9.remove()',
1693+
'<div>10.remove()',
1694+
'<div>3146250.appendChild(<div>1)',
1695+
'<div>3462501.appendChild(<div>2)',
1696+
'<div>3465012.appendChild(<div>3)',
1697+
'<div>4650123.appendChild(<div>4)',
1698+
'<div>6501234.appendChild(<div>5)',
1699+
'<div>6012345.appendChild(<div>6)'
1700+
]);
1701+
clearLog();
1702+
});
16391703
});

0 commit comments

Comments
 (0)