each-branch-manager
Rich Harris 3 days ago
parent 3c133bb5ce
commit e075fb2ede

@ -42,6 +42,7 @@ import { active_effect, get } from '../../runtime.js';
import { DEV } from 'esm-env'; import { DEV } from 'esm-env';
import { derived_safe_equal } from '../../reactivity/deriveds.js'; import { derived_safe_equal } from '../../reactivity/deriveds.js';
import { current_batch } from '../../reactivity/batch.js'; import { current_batch } from '../../reactivity/batch.js';
import { log_effect_tree, root } from '../../dev/debug.js';
/** /**
* The row of a keyed each block that is currently updating. We track this * The row of a keyed each block that is currently updating. We track this
@ -151,9 +152,6 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
/** @type {V[]} */ /** @type {V[]} */
var array; var array;
/** @type {Effect} */
var each_effect;
function commit() { function commit() {
reconcile(each_effect, array, state, anchor, render_fn, flags, get_key, get_collection); reconcile(each_effect, array, state, anchor, render_fn, flags, get_key, get_collection);
@ -172,10 +170,9 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
} }
} }
block(() => { var first_run = true;
// store a reference to the effect so that we can update the start/end nodes in reconciliation
each_effect ??= /** @type {Effect} */ (active_effect);
var each_effect = block(() => {
array = /** @type {V[]} */ (get(each_array)); array = /** @type {V[]} */ (get(each_array));
var length = array.length; var length = array.length;
@ -202,31 +199,41 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
} }
} }
// this is separate to the previous block because `hydrating` might change var keys = new Set();
if (hydrating) { var batch = /** @type {Batch} */ (current_batch);
/** @type {EachItem | null} */ var prev = null;
var prev = null; var defer = should_defer_append();
/** @type {EachItem} */ for (var i = 0; i < length; i += 1) {
var item; if (
hydrating &&
for (var i = 0; i < length; i++) { hydrate_node.nodeType === COMMENT_NODE &&
if ( /** @type {Comment} */ (hydrate_node).data === HYDRATION_END
hydrate_node.nodeType === COMMENT_NODE && ) {
/** @type {Comment} */ (hydrate_node).data === HYDRATION_END // The server rendered fewer items than expected,
) { // so break out and continue appending non-hydrated items
// The server rendered fewer items than expected, anchor = /** @type {Comment} */ (hydrate_node);
// so break out and continue appending non-hydrated items mismatch = true;
anchor = /** @type {Comment} */ (hydrate_node); set_hydrating(false);
mismatch = true; break;
set_hydrating(false); }
break;
var value = array[i];
var key = get_key(value, i);
var item = first_run ? null : state.onscreen.get(key) ?? state.offscreen.get(key);
if (item) {
// update before reconciliation, to trigger any async updates
if ((flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0) {
update_item(item, value, i, flags);
} }
var value = array[i]; batch.skipped_effects.delete(item.e);
var key = get_key(value, i); } else {
console.log('creating', key);
item = create_item( item = create_item(
hydrate_node, first_run ? (hydrating ? hydrate_node : anchor) : null,
state, state,
prev, prev,
null, null,
@ -235,66 +242,40 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
i, i,
render_fn, render_fn,
flags, flags,
get_collection get_collection,
defer
); );
state.onscreen.set(key, item);
prev = item;
}
// remove excess nodes
if (length > 0) {
set_hydrate_node(skip_nodes());
}
}
if (hydrating) { if (first_run) {
if (length === 0 && fallback_fn) { if (prev === null) {
fallback = branch(() => fallback_fn(anchor)); state.first = item;
}
} else {
if (should_defer_append()) {
var keys = new Set();
var batch = /** @type {Batch} */ (current_batch);
for (i = 0; i < length; i += 1) {
value = array[i];
key = get_key(value, i);
var existing = state.onscreen.get(key) ?? state.offscreen.get(key);
if (existing) {
// update before reconciliation, to trigger any async updates
if ((flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0) {
update_item(existing, value, i, flags);
}
} else { } else {
item = create_item( prev.next = item;
null,
state,
null,
null,
value,
key,
i,
render_fn,
flags,
get_collection,
true
);
state.offscreen.set(key, item);
} }
keys.add(key); prev = item;
state.onscreen.set(key, item);
} else {
state.offscreen.set(key, item);
} }
}
for (const [key, item] of state.onscreen) { keys.add(key);
if (!keys.has(key)) { }
batch.skipped_effects.add(item.e);
} // remove excess nodes
} if (hydrating && length > 0) {
set_hydrate_node(skip_nodes());
}
for (const [key, item] of state.onscreen) {
if (!keys.has(key)) {
batch.skipped_effects.add(item.e);
}
}
if (!first_run) {
if (defer) {
batch.oncommit(commit); batch.oncommit(commit);
} else { } else {
commit(); commit();
@ -315,6 +296,8 @@ export function each(node, flags, get_collection, get_key, render_fn, fallback_f
get(each_array); get(each_array);
}); });
first_run = false;
if (hydrating) { if (hydrating) {
anchor = hydrate_node; anchor = hydrate_node;
} }
@ -338,7 +321,7 @@ function reconcile(each_effect, array, state, anchor, render_fn, flags, get_key,
var should_update = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0; var should_update = (flags & (EACH_ITEM_REACTIVE | EACH_INDEX_REACTIVE)) !== 0;
var length = array.length; var length = array.length;
var items = state.onscreen; var onscreen = state.onscreen;
var first = state.first; var first = state.first;
var current = first; var current = first;
@ -373,7 +356,7 @@ function reconcile(each_effect, array, state, anchor, render_fn, flags, get_key,
for (i = 0; i < length; i += 1) { for (i = 0; i < length; i += 1) {
value = array[i]; value = array[i];
key = get_key(value, i); key = get_key(value, i);
item = items.get(key); item = onscreen.get(key);
if (item !== undefined) { if (item !== undefined) {
item.a?.measure(); item.a?.measure();
@ -386,14 +369,14 @@ function reconcile(each_effect, array, state, anchor, render_fn, flags, get_key,
value = array[i]; value = array[i];
key = get_key(value, i); key = get_key(value, i);
item = items.get(key); item = onscreen.get(key);
if (item === undefined) { if (item === undefined) {
var pending = state.offscreen.get(key); var pending = state.offscreen.get(key);
if (pending !== undefined) { if (pending !== undefined) {
state.offscreen.delete(key); state.offscreen.delete(key);
items.set(key, pending); onscreen.set(key, pending);
var next = prev ? prev.next : current; var next = prev ? prev.next : current;
@ -419,7 +402,7 @@ function reconcile(each_effect, array, state, anchor, render_fn, flags, get_key,
); );
} }
items.set(key, prev); onscreen.set(key, prev);
matched = []; matched = [];
stashed = []; stashed = [];
@ -643,13 +626,14 @@ function create_item(
fragment.append((anchor = create_text())); fragment.append((anchor = create_text()));
} }
item.e = branch(() => render_fn(/** @type {Node} */ (anchor), v, i, get_collection), hydrating); item.e = branch(() => render_fn(/** @type {Node} */ (anchor), v, i, get_collection));
item.e.prev = prev && prev.e; item.e.prev = prev && prev.e;
item.e.next = next && next.e; item.e.next = next && next.e;
if (prev === null) { if (prev === null) {
if (!deferred) { if (!deferred) {
// TODO move this into block effect?
state.first = item; state.first = item;
} }
} else { } else {

@ -171,6 +171,8 @@ function create_effect(type, fn, sync, push = true) {
(derived.effects ??= []).push(e); (derived.effects ??= []).push(e);
} }
} }
} else {
console.trace('not pushing');
} }
return effect; return effect;
@ -386,6 +388,7 @@ export function block(fn, flags = 0) {
return effect; return effect;
} }
// TODO i think we don't need `push` any more?
/** /**
* @param {(() => void)} fn * @param {(() => void)} fn
* @param {boolean} [push] * @param {boolean} [push]

Loading…
Cancel
Save