pull/10798/head
Rich Harris 2 years ago
parent e00141739e
commit c29a688f7d

@ -43,6 +43,11 @@ const LIS_BLOCK = -2;
function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, reconcile_fn) {
const is_controlled = (flags & EACH_IS_CONTROLLED) !== 0;
// If we're in an keyed each block, ensure the items are all wrapped in sources
if ((flags & EACH_KEYED) !== 0) {
flags |= EACH_ITEM_REACTIVE;
}
/** @type {import('#client').EachBlock} */
const block = {
// dom
@ -58,59 +63,14 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
hydrate_block_anchor(anchor_node, is_controlled);
/** @type {V[]} */
let array;
/**
* Whether or not there was a "rendered fallback but want to render items" (or vice versa) hydration mismatch.
* Needs to be a `let` or else it isn't treeshaken out
*/
let mismatch = false;
const create_fallback_effect = () => {
return render_effect(
() => {
let anchor = anchor_node;
const is_controlled = (block.f & EACH_IS_CONTROLLED) !== 0;
if (is_controlled) {
// If the each block is controlled, then the anchor node will be the surrounding
// element in which the each block is rendered, which requires certain handling
// depending on whether we're in hydration mode or not
if (!hydrating) {
// Create a new anchor on the fly because there's none due to the optimization
// TODO this will happen every time the fallback renders...
anchor = empty();
anchor_node.appendChild(anchor);
} else {
// In case of hydration the anchor will be the first child of the surrounding element
anchor = /** @type {Comment} */ (anchor.firstChild);
}
}
/** @type {(anchor: Node) => void} */ (fallback_fn)(anchor);
var dom = block.d; // TODO would be nice if this was just returned from the managed effect function...
return () => {
if (dom !== null) {
remove(dom);
dom = null;
}
};
},
block,
true
);
};
/** @type {import('#client').Effect | null} */
let fallback = null;
const each = render_effect(
() => {
/** @type {V[]} */
const maybe_array = collection();
array = is_array(maybe_array)
var array = is_array(maybe_array)
? maybe_array
: maybe_array == null
? []
@ -120,6 +80,18 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
const length = array.length;
const is_controlled = (block.f & EACH_IS_CONTROLLED) !== 0;
// If we are working with an array that isn't proxied or frozen, then remove strict equality and ensure the items
// are treated as reactive, so they get wrapped in a signal.
var flags = block.f;
if ((flags & EACH_IS_STRICT_EQUALS) !== 0 && !is_frozen(array) && !(STATE_SYMBOL in array)) {
flags ^= EACH_IS_STRICT_EQUALS;
}
/** `true` if there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
let mismatch = false;
if (hydrating) {
var is_else =
/** @type {Comment} */ (current_hydration_fragment?.[0])?.data === 'ssr:each_else';
@ -135,38 +107,8 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
}
}
if (fallback_fn !== null) {
if (length === 0) {
if (fallback) {
resume_effect(fallback);
} else {
fallback = create_fallback_effect();
}
} else if (fallback !== null) {
pause_effect(fallback, () => {
fallback = null;
});
}
}
const is_controlled = (block.f & EACH_IS_CONTROLLED) !== 0;
// If we are working with an array that isn't proxied or frozen, then remove strict equality and ensure the items
// are treated as reactive, so they get wrapped in a signal.
var flags = block.f;
if ((flags & EACH_IS_STRICT_EQUALS) !== 0 && !is_frozen(array) && !(STATE_SYMBOL in array)) {
flags ^= EACH_IS_STRICT_EQUALS;
// Additionally if we're in an keyed each block, we'll need ensure the items are all wrapped in signals.
if ((flags & EACH_KEYED) !== 0 && (flags & EACH_ITEM_REACTIVE) === 0) {
flags ^= EACH_ITEM_REACTIVE;
}
}
// this is separate to the previous block because `hydrating` might change
if (hydrating) {
/** `true` if there was a hydration mismatch. Needs to be a `let` or else it isn't treeshaken out */
let mismatch = false;
var b_blocks = [];
// Hydrate block
@ -197,25 +139,67 @@ function each(anchor_node, collection, flags, key_fn, render_fn, fallback_fn, re
remove_excess_hydration_nodes(hydration_list, hydrating_node);
block.v = b_blocks;
}
if (mismatch) {
// Server rendered less nodes than the client -> set empty array so that Svelte continues to operate in hydration mode
reconcile_fn(array, block, anchor_node, is_controlled, render_fn, flags, keys);
set_current_hydration_fragment([]);
}
} else {
if (!hydrating) {
reconcile_fn(array, block, anchor_node, is_controlled, render_fn, flags, keys);
}
if (fallback_fn !== null) {
if (length === 0) {
if (fallback) {
resume_effect(fallback);
} else {
fallback = render_effect(
() => {
let anchor = anchor_node;
const is_controlled = (block.f & EACH_IS_CONTROLLED) !== 0;
if (is_controlled) {
// If the each block is controlled, then the anchor node will be the surrounding
// element in which the each block is rendered, which requires certain handling
// depending on whether we're in hydration mode or not
if (!hydrating) {
// Create a new anchor on the fly because there's none due to the optimization
// TODO this will happen every time the fallback renders...
anchor = empty();
anchor_node.appendChild(anchor);
} else {
// In case of hydration the anchor will be the first child of the surrounding element
anchor = /** @type {Comment} */ (anchor.firstChild);
}
}
/** @type {(anchor: Node) => void} */ (fallback_fn)(anchor);
var dom = block.d; // TODO would be nice if this was just returned from the managed effect function...
return () => {
if (dom !== null) {
remove(dom);
dom = null;
}
};
},
block,
true
);
}
} else if (fallback !== null) {
pause_effect(fallback, () => {
fallback = null;
});
}
}
if (mismatch) {
// Set a fragment so that Svelte continues to operate in hydration mode
set_current_hydration_fragment([]);
}
},
block,
false
);
if (mismatch) {
// Set a fragment so that Svelte continues to operate in hydration mode
set_current_hydration_fragment([]);
}
each.ondestroy = () => {
for (const b of block.v) {
if (b.d !== null) {

Loading…
Cancel
Save