1- import { Accessor, JSX, createMemo, createRoot, onCleanup, untrack } from "solid-js";
1+ import { Accessor, JSX, createMemo, createRoot, onCleanup } from "solid-js";
22import { toFunction } from "./common.js";
33
44/**
@@ -23,60 +23,68 @@ export function repeat<T>(
2323 mapFn: (i: number) => T,
2424 options: { fallback?: Accessor<T> } = {},
2525): Accessor<T[]> {
26- let disposers: (() => void)[] = [],
27- items: T[] = [],
28- prevLen = 0;
29-
30- onCleanup(() => disposers.forEach(f => f()));
26+ let prev: readonly T[] = [];
27+ let prevLen: number | undefined;
28+ const disposers: (() => void)[] = [];
29+ onCleanup(() => {
30+ for (let index = 0; index < disposers.length; index++) {
31+ disposers[index]!();
32+ }
33+ });
3134
32- const mapLength = (len: number): T[] => {
33- if (len === 0) {
34- disposers.forEach(f => f());
35+ // Truncate toward zero and force positive
36+ const memoLen = createMemo(() => Math.max(times() | 0, 0));
3537
36- if (options.fallback)
37- return createRoot(dispose => {
38- disposers = [dispose];
39- return (items = [options.fallback!()]);
40- });
38+ return function mapLength(): T[] {
39+ const len = memoLen();
40+ if (len === prevLen) return prev as T[];
4141
42- disposers = [];
43- return (items = []);
42+ // Dispose of fallback or unnecessarry elements
43+ if (prevLen === 0) disposers[0]?.();
44+ else {
45+ for (let index = len; index < disposers.length; index++) {
46+ disposers[index]!();
47+ }
4448 }
4549
46- if (prevLen === 0) {
47- // after fallback case:
48- if (disposers[0]) disposers[0]();
49- for (let i = 0; i < len; i++) items[i] = createRoot(mapper.bind(void 0, i));
50- return items;
51- }
50+ // The following prefers to use `prev.slice` to
51+ // preserve any array element kind optimizations
52+ // the runtime has made.
5253
53- {
54- const diff = prevLen - len;
55- if (diff > 0) {
56- for (let i = prevLen - 1; i >= len; i--) disposers[i]!();
57- items.splice(len, diff);
58- disposers.splice(len, diff);
59- return items;
54+ if (len === 0) {
55+ const fallback = options.fallback;
56+ if (fallback) {
57+ // Show fallback if available
58+ const next = prev.slice(0, 1);
59+ next[0] = createRoot(dispose => {
60+ disposers[0] = dispose;
61+ return fallback();
62+ });
63+
64+ disposers.length = 1;
65+ prevLen = 0;
66+ return (prev = next);
67+ } else {
68+ // Show empty array, otherwise
69+ disposers.length = 0;
70+ prevLen = 0;
71+ return (prev = prev.slice(0, 0));
6072 }
6173 }
6274
63- for (let i = prevLen; i < len; i++) items[i] = createRoot(mapper.bind(void 0, i));
64- return items;
65- };
75+ const next = prev.slice(0, len);
6676
67- const mapper = (index: number, dispose: () => void): T => {
68- disposers[index] = dispose;
69- return mapFn(index);
70- };
77+ // Create new elements as needed
78+ for (let index = prevLen ?? 0; index < len; index++) {
79+ next[index] = createRoot(dispose => {
80+ disposers[index] = dispose;
81+ return mapFn(index);
82+ });
83+ }
7184
72- const memoLen = createMemo(() => Math.floor(Math.max(times(), 0)));
73- return () => {
74- const len = memoLen();
75- return untrack(() => {
76- const newItems = mapLength(len);
77- prevLen = len;
78- return newItems;
79- });
85+ disposers.length = len;
86+ prevLen = len;
87+ return (prev = next);
8088 };
8189}
8290
0 commit comments