From c38f02165d0b576f44b4e984e5db400916709599 Mon Sep 17 00:00:00 2001 From: Joseph Caburnay Date: Wed, 3 Jan 2024 18:23:36 +0100 Subject: [PATCH] add new test for computed that sorts an array I had a discussion with SEB and he told me about auto-sort compute. He said that it would be better if we could implement automatic in-place sorting without triggering the reactivity. However, this PR can't actually handle that because this PR is just an extension of the existing reactivity, it doesn't actually modify it. So if inplace sort will result to multiple mutations, then that will be the number of times the callback will be called. Even inside a `computed` that's introduced in this PR. Nevertheless, compute that depends on sorted array is still useful, it's just that the sorting should not be done in-place. This commit introduces an example where a computed value that returns a sorted array is only called once (in a batched context) even though multiple mutations are made to the array. --- tests/computed.test.ts | 87 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+) diff --git a/tests/computed.test.ts b/tests/computed.test.ts index 7f7e1ac0a..4e8e57ef3 100644 --- a/tests/computed.test.ts +++ b/tests/computed.test.ts @@ -183,6 +183,93 @@ describe("computed - with effect", () => { expect(orderTotal).toEqual(244.8); expectOrderComputeCounts({ itemTotal: 0, orderTotal: 1 }); }); + + test("computed sorted array", async () => { + let sortingCount = 0; + const expectSortingCount = (expected: number) => { + expect(sortingCount).toBe(expected); + sortingCount = 0; + }; + + const array = reactive([ + 52, 26, 71, 63, 72, 57, 71, 11, 17, 30, 52, 90, 14, 33, 86, 13, 62, 34, 99, 61, 21, 92, 95, + 99, 0, 92, 6, 35, 95, 39, 87, 30, 50, 74, 21, 67, 34, 98, 99, 46, 85, 63, 41, 56, 18, 43, 23, + 59, 52, 12, + ]); + + const sortedArray = computed((a: typeof array) => { + sortingCount++; + const copy = [...a]; + return copy.sort((a, b) => a - b); + }); + + const range = computed((a: typeof array) => { + a = sortedArray(a); + return a[a.length - 1] - a[0]; + }); + + const average = computed((a: typeof array) => { + let sum = 0; + for (let i = 0; i < a.length; i++) { + sum += a[i]; + } + return Math.trunc(sum / a.length); + }); + + const min = computed((a: typeof array) => { + a = sortedArray(a); + return a[0]; + }); + + const max = computed((a: typeof array) => { + a = sortedArray(a); + return a[a.length - 1]; + }); + + const statTotal = computed((a: typeof array) => { + return range(a) + average(a) + min(a) + max(a); + }); + + let val = 0; + effect( + array, + batched((a) => { + val = statTotal(a) + statTotal(a) + statTotal(a); + }) + ); + + await nextMicroTick(); + + expectSortingCount(1); + expect(val).toEqual(250 * 3); + + array.push(99, 100); + await nextMicroTick(); + + expectSortingCount(1); + expect(val).toEqual(254 * 3); + + array.push(-50); + await nextMicroTick(); + + expectSortingCount(1); + expect(val).toEqual(252 * 3); + + // After some mutations, the original order is kept because the `sortedArray` getter makes a copy of the array before sorting. + const arrayCopy = [...array]; + for (let i = 0; i < array.length; i++) { + expect(array[i]).toEqual(arrayCopy[i]); + } + + // Sort will perform so many mutations in the original array. + // This will register several of recomputations. + // But since the effect is batched, it will only be recomputed once. + array.sort((a, b) => b - a); + await nextMicroTick(); + + expectSortingCount(1); + expect(val).toEqual(252 * 3); + }); }); describe("computed - with components", () => {