Skip to content

Commit e5a1163

Browse files
committed
fix(core): journal per chain of rerenders
This should ensure that related changes happen together.
1 parent 89a07ff commit e5a1163

File tree

3 files changed

+37
-17
lines changed

3 files changed

+37
-17
lines changed

packages/qwik/src/core/client/chore-array.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ export class ChoreArray extends Array<Chore> {
3232
if (existing.$payload$ !== value.$payload$) {
3333
existing.$payload$ = value.$payload$;
3434
}
35+
if (value.$type$ === ChoreType.COMPONENT && value.$extra$) {
36+
if (!existing.$extra$) {
37+
existing.$extra$ = value.$extra$;
38+
}
39+
}
3540
return idx;
3641
}
3742

packages/qwik/src/core/client/vnode-diff.ts

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,6 @@ import { serializeAttribute } from '../shared/utils/styles';
5151
import { isArray, type ValueOrPromise } from '../shared/utils/types';
5252
import { trackSignalAndAssignHost } from '../use/use-core';
5353
import { TaskFlags, cleanupTask, isTask } from '../use/use-task';
54-
import type { DomContainer } from './dom-container';
5554
import { VNodeFlags, type ClientAttrs, type ClientContainer } from './types';
5655
import { mapApp_findIndx, mapArray_set } from './util-mapArray';
5756
import {
@@ -82,14 +81,16 @@ import {
8281
import type { ElementVNode, TextVNode, VNode, VirtualVNode } from './vnode-impl';
8382
import { getAttributeNamespace, getNewElementNamespaceData } from './vnode-namespace';
8483

84+
export type VNodeJournalRef = { $journal$: VNodeJournal; $refCount$: number };
85+
8586
export const vnode_diff = (
8687
container: ClientContainer,
8788
jsxNode: JSXChildren,
8889
vStartNode: VNode,
89-
scopedStyleIdPrefix: string | null
90+
scopedStyleIdPrefix: string | null,
91+
journalRef: VNodeJournalRef = { $journal$: [], $refCount$: 1 }
9092
) => {
91-
let journal = (container as DomContainer).$journal$;
92-
93+
let journal = journalRef.$journal$;
9394
/**
9495
* Stack is used to keep track of the state of the traversal.
9596
*
@@ -231,6 +232,7 @@ export const vnode_diff = (
231232
}
232233
} else if (jsxValue === (SkipRender as JSXChildren)) {
233234
// do nothing, we are skipping this node
235+
// Note, this probably breaks async stuff
234236
journal = [];
235237
} else {
236238
expectText('');
@@ -559,6 +561,11 @@ export const vnode_diff = (
559561
diff(jsxNode, vHostNode);
560562
}
561563
}
564+
journalRef.$refCount$--;
565+
if (journalRef.$refCount$ === 0) {
566+
// all done with the journal, pass it to the container
567+
container.$journal$.push(...journalRef.$journal$);
568+
}
562569
}
563570

564571
function expectNoChildren() {
@@ -1268,7 +1275,8 @@ export const vnode_diff = (
12681275
* deleted.
12691276
*/
12701277
(host as VirtualVNode).flags &= ~VNodeFlags.Deleted;
1271-
container.$scheduler$(ChoreType.COMPONENT, host, componentQRL, vNodeProps);
1278+
journalRef.$refCount$++;
1279+
container.$scheduler$(ChoreType.COMPONENT, host, componentQRL, vNodeProps, journalRef);
12721280
}
12731281
}
12741282
descendContentToProject(jsxNode.children, host);

packages/qwik/src/core/shared/scheduler.ts

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,17 @@
8080
* declaration order within component.
8181
*/
8282

83+
import { ChoreArray, choreComparator } from '../client/chore-array';
8384
import { type DomContainer } from '../client/dom-container';
8485
import { VNodeFlags, type ClientContainer } from '../client/types';
8586
import { VNodeJournalOpCode, vnode_isVNode } from '../client/vnode';
86-
import { vnode_diff } from '../client/vnode-diff';
87+
import { vnode_diff, type VNodeJournalRef } from '../client/vnode-diff';
88+
import type { ElementVNode, VirtualVNode } from '../client/vnode-impl';
89+
import { AsyncComputedSignalImpl } from '../reactive-primitives/impl/async-computed-signal-impl';
8790
import { ComputedSignalImpl } from '../reactive-primitives/impl/computed-signal-impl';
8891
import { WrappedSignalImpl } from '../reactive-primitives/impl/wrapped-signal-impl';
8992
import { isSignal, type Signal } from '../reactive-primitives/signal.public';
93+
import { isSsrNode } from '../reactive-primitives/subscriber';
9094
import type { NodePropPayload } from '../reactive-primitives/subscription-data';
9195
import {
9296
SignalFlags,
@@ -97,6 +101,7 @@ import {
97101
} from '../reactive-primitives/types';
98102
import { scheduleEffects } from '../reactive-primitives/utils';
99103
import { type ISsrNode, type SSRContainer } from '../ssr/ssr-types';
104+
import { invoke, newInvokeContext } from '../use/use-core';
100105
import { runResource, type ResourceDescriptor } from '../use/use-resource';
101106
import {
102107
Task,
@@ -110,23 +115,18 @@ import { executeComponent } from './component-execution';
110115
import type { OnRenderFn } from './component.public';
111116
import type { Props } from './jsx/jsx-runtime';
112117
import type { JSXOutput } from './jsx/types/jsx-node';
118+
import { createNextTick } from './platform/next-tick';
113119
import { isServerPlatform } from './platform/platform';
114120
import { type QRLInternal } from './qrl/qrl-class';
121+
import { findBlockingChore, findBlockingChoreForVisible } from './scheduler-rules';
115122
import { SsrNodeFlags, type Container, type HostElement } from './types';
116123
import { ChoreType } from './util-chore-type';
124+
import { logWarn } from './utils/log';
117125
import { QScopedStyle } from './utils/markers';
118126
import { isPromise, maybeThen, retryOnPromise, safeCall } from './utils/promises';
119127
import { addComponentStylePrefix } from './utils/scoped-styles';
120128
import { serializeAttribute } from './utils/styles';
121129
import { type ValueOrPromise } from './utils/types';
122-
import { invoke, newInvokeContext } from '../use/use-core';
123-
import { findBlockingChore, findBlockingChoreForVisible } from './scheduler-rules';
124-
import { createNextTick } from './platform/next-tick';
125-
import { AsyncComputedSignalImpl } from '../reactive-primitives/impl/async-computed-signal-impl';
126-
import { isSsrNode } from '../reactive-primitives/subscriber';
127-
import { logWarn } from './utils/log';
128-
import type { ElementVNode, VirtualVNode } from '../client/vnode-impl';
129-
import { ChoreArray, choreComparator } from '../client/chore-array';
130130

131131
// Turn this on to get debug output of what the scheduler is doing.
132132
const DEBUG: boolean = false;
@@ -161,6 +161,7 @@ export interface Chore<T extends ChoreType = ChoreType> {
161161
$resolve$: ((value: any) => void) | undefined;
162162
$reject$: ((reason?: any) => void) | undefined;
163163
$returnValue$: ValueOrPromise<ChoreReturnValue<T>>;
164+
$extra$?: unknown;
164165
}
165166

166167
export type Scheduler = ReturnType<typeof createScheduler>;
@@ -243,7 +244,8 @@ export const createScheduler = (
243244
type: ChoreType.COMPONENT,
244245
host: HostElement,
245246
qrl: QRLInternal<OnRenderFn<unknown>>,
246-
props: Props | null
247+
props: Props | null,
248+
journalRef?: VNodeJournalRef
247249
): Chore<ChoreType.COMPONENT>;
248250
function schedule(
249251
type: ChoreType.NODE_DIFF,
@@ -263,7 +265,8 @@ export const createScheduler = (
263265
type: T,
264266
hostOrTask: HostElement | Task | null = null,
265267
targetOrQrl: ChoreTarget | string | null = null,
266-
payload: any = null
268+
payload: unknown = null,
269+
extra?: unknown
267270
): Chore<T> | null {
268271
if (type === ChoreType.WAIT_FOR_QUEUE && drainChore) {
269272
return drainChore as Chore<T>;
@@ -293,6 +296,9 @@ export const createScheduler = (
293296
$reject$: undefined,
294297
$returnValue$: null!,
295298
};
299+
if (extra !== undefined) {
300+
(chore as any).$extra$ = extra;
301+
}
296302

297303
if (type === ChoreType.WAIT_FOR_QUEUE) {
298304
getChorePromise(chore);
@@ -659,7 +665,8 @@ This is often caused by modifying a signal in an already rendered component duri
659665
container as ClientContainer,
660666
jsx,
661667
host as VirtualVNode,
662-
addComponentStylePrefix(styleScopedId)
668+
addComponentStylePrefix(styleScopedId),
669+
(chore as any).$extra$
663670
)
664671
);
665672
}

0 commit comments

Comments
 (0)