Skip to content

Commit ab39944

Browse files
committed
feat: cache parsed InitialState results to Map for later use
Signed-off-by: Maksim Sukharev <antreesy.web@gmail.com>
1 parent a7d4fa6 commit ab39944

3 files changed

Lines changed: 45 additions & 9 deletions

File tree

lib/env.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/**
2+
* SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
3+
* SPDX-License-Identifier: GPL-3.0-or-later
4+
*/
5+
6+
declare interface Window {
7+
_nc_initial_state: Map<string, unknown>
8+
}

lib/index.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,24 @@
33
* SPDX-License-Identifier: GPL-3.0-or-later
44
*/
55

6+
if (!window._nc_initial_state) {
7+
window._nc_initial_state = new Map<string, unknown>()
8+
}
9+
610
/**
711
* @param app app ID, e.g. "mail"
812
* @param key name of the property
913
* @param fallback optional parameter to use as default value
1014
* @throws if the key can't be found
1115
*/
1216
export function loadState<T>(app: string, key: string, fallback?: T): T {
13-
const elem = <HTMLInputElement>document.querySelector(`#initial-state-${app}-${key}`)
17+
const selector = `#initial-state-${app}-${key}`
18+
if (window._nc_initial_state.has(selector)) {
19+
return window._nc_initial_state.get(selector) as T
20+
}
21+
22+
const elem = document.querySelector<HTMLInputElement>(selector)
23+
1424
if (elem === null) {
1525
if (fallback !== undefined) {
1626
return fallback
@@ -19,7 +29,9 @@ export function loadState<T>(app: string, key: string, fallback?: T): T {
1929
}
2030

2131
try {
22-
return JSON.parse(atob(elem.value))
32+
const parsedValue = JSON.parse(atob(elem.value))
33+
window._nc_initial_state.set(selector, parsedValue)
34+
return parsedValue
2335
} catch (e) {
2436
throw new Error(`Could not parse initial state ${key} of ${app}`)
2537
}

test/index.test.ts

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,18 @@
22
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
33
* SPDX-License-Identifier: GPL-3.0-or-later
44
*/
5-
import { expect, test } from 'vitest'
5+
import { expect, test, vi } from 'vitest'
66
import { loadState } from '../lib'
77

8+
function appendInput(app: string, key: string, value: string) {
9+
const input = document.createElement('input')
10+
input.setAttribute('type', 'hidden')
11+
input.setAttribute('id', `initial-state-${app}-${key}`)
12+
input.setAttribute('value', btoa(JSON.stringify(value)))
13+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
14+
document.querySelector('body')!.appendChild(input)
15+
}
16+
817
test('throw if nothing found', () => {
918
expect(() => loadState('test', 'key')).toThrow(new Error('Could not find initial state key of test'))
1019
})
@@ -14,14 +23,21 @@ test('return default if provided', () => {
1423
})
1524

1625
test('find correct value', () => {
17-
const input = document.createElement('input')
18-
input.setAttribute('type', 'hidden')
19-
input.setAttribute('id', 'initial-state-test-key')
20-
input.setAttribute('value', btoa(JSON.stringify('foo')))
21-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
22-
document.querySelector('body')!.appendChild(input)
26+
appendInput('test', 'key', 'foo')
2327

2428
const state = loadState('test', 'key')
2529

2630
expect(state).toBe('foo')
2731
})
32+
33+
test('returns cached value with consequent calls', () => {
34+
vi.spyOn(JSON, 'parse')
35+
36+
appendInput('test', 'cachedKey', 'foo')
37+
38+
for (let i = 0; i < 10; i++) {
39+
loadState('test', 'cachedKey')
40+
}
41+
42+
expect(JSON.parse).toHaveBeenCalledTimes(1)
43+
})

0 commit comments

Comments
 (0)