Skip to content

Commit 348974b

Browse files
committed
fix(storage): types
docs(storage): resource usage
1 parent 2249f79 commit 348974b

File tree

4 files changed

+45
-45
lines changed

4 files changed

+45
-45
lines changed

.changeset/short-memes-count.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@solid-primitives/storage": minor
3+
---
4+
5+
types and resource usage

packages/storage/README.md

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,13 +56,17 @@ Instead of wrapping the resource itself, it is far simpler to use the `storage`
5656
persisted signal or [deep signal](../resource/#createdeepsignal):
5757

5858
```ts
59-
const [resource] = createResource(fetcher, { storage: makePersisted(createSignal()) });
59+
const [resource] = createResource(fetcher, {
60+
storage: value => makePersisted(createSignal(value)),
61+
});
6062
```
6163

6264
If you are using an asynchronous storage to persist the state of a resource, it might receive an update due to being
6365
initialized from the storage before or after the fetcher resolved. If the initialization resolves after the fetcher, its
6466
result is discarded not to overwrite more current data.
6567

68+
If instead of a signal you want to use a store, consider using the `makeDeepSignal` primitive from the `resource` package.
69+
6670
### Using `makePersisted` with Suspense
6771

6872
In case you are using an asynchronous storage and want the initialisation mesh into Suspense instead of mixing it with Show, we provide the output of the initialisation as third part of the returned tuple:

packages/storage/src/persisted.ts

Lines changed: 34 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Accessor, Setter, Signal } from "solid-js";
1+
import type { Setter, Signal } from "solid-js";
22
import { createUniqueId, untrack } from "solid-js";
33
import { isServer, isDev } from "solid-js/web";
44
import type { SetStoreFunction, Store } from "solid-js/store";
@@ -70,17 +70,7 @@ export type PersistenceOptions<T, O extends Record<string, any> | undefined> = {
7070
storageOptions?: O;
7171
});
7272

73-
export type SignalInput = Signal<any> | [Store<any>, SetStoreFunction<any>];
74-
75-
export type SignalType<S extends SignalInput> =
76-
S extends Signal<infer T> ? T : S extends [Store<infer T>, SetStoreFunction<infer T>] ? T : never;
77-
78-
export type PersistedState<S extends SignalInput> =
79-
S extends Signal<infer T>
80-
? [get: Accessor<T>, set: Setter<T>, init: Promise<string> | string | null]
81-
: S extends [Store<infer T>, SetStoreFunction<infer T>]
82-
? [get: Store<T>, set: SetStoreFunction<T>, init: Promise<string> | string | null]
83-
: never;
73+
export type PersistedState<S> = S & { 2: Promise<string> | string | null };
8474

8575
/**
8676
* Persists a signal, store or similar API
@@ -102,26 +92,27 @@ export type PersistedState<S extends SignalInput> =
10292
* @param {PersistenceOptions<T, O>} options - The options for persistence.
10393
* @returns {PersistedState<T>} - The persisted signal or store.
10494
*/
105-
export function makePersisted<S extends SignalInput>(
106-
signal: S,
107-
options?: PersistenceOptions<SignalType<S>, undefined>,
108-
): PersistedState<S>;
109-
export function makePersisted<S extends SignalInput, O extends Record<string, any>>(
95+
export function makePersisted<T, S extends Signal<T> | [Store<T>, SetStoreFunction<T>]>(
11096
signal: S,
111-
options: PersistenceOptions<SignalType<S>, O>,
97+
options?: PersistenceOptions<T, undefined>,
11298
): PersistedState<S>;
11399
export function makePersisted<
114-
S extends SignalInput,
100+
T,
101+
S extends Signal<T> | [Store<T>, SetStoreFunction<T>],
102+
O extends Record<string, any>,
103+
>(signal: S, options: PersistenceOptions<T, O>): PersistedState<S>;
104+
export function makePersisted<
105+
T,
106+
S extends Signal<T> | [Store<T>, SetStoreFunction<T>],
115107
O extends Record<string, any> | undefined,
116-
T = SignalType<S>,
117108
>(
118109
signal: S,
119110
options: PersistenceOptions<T, O> = {} as PersistenceOptions<T, O>,
120111
): PersistedState<S> {
121112
const storage = options.storage || (globalThis.localStorage as Storage | undefined);
122113
const name = options.name || `storage-${createUniqueId()}`;
123114
if (!storage) {
124-
return [signal[0], signal[1], null] as PersistedState<S>;
115+
return Object.assign(signal, { 2: null });
125116
}
126117
const storageOptions = (options as unknown as { storageOptions: O }).storageOptions;
127118
const serialize: (data: T) => string = options.serialize || JSON.stringify.bind(JSON);
@@ -167,28 +158,28 @@ export function makePersisted<
167158
});
168159
}
169160

170-
return [
171-
signal[0],
172-
typeof signal[0] === "function"
173-
? (value?: T | ((prev: T) => T)) => {
174-
const output = (signal[1] as Setter<T>)(value as any);
175-
const serialized: string | null | undefined =
176-
value != null ? serialize(output) : (value as null | undefined);
177-
options.sync?.[1](name, serialized);
178-
if (serialized != null) storage.setItem(name, serialized, storageOptions);
179-
else storage.removeItem(name, storageOptions);
180-
unchanged = false;
181-
return output;
182-
}
183-
: (...args: any[]) => {
184-
(signal[1] as any)(...args);
185-
const value = serialize(untrack(() => signal[0]));
186-
options.sync?.[1](name, value);
187-
storage.setItem(name, value, storageOptions);
188-
unchanged = false;
189-
},
190-
init,
191-
] as PersistedState<S>;
161+
return Object.assign([], signal, {
162+
1:
163+
typeof signal[0] === "function"
164+
? (value?: T | ((prev: T) => T)) => {
165+
const output = (signal[1] as Setter<T>)(value as any);
166+
const serialized: string | null | undefined =
167+
value != null ? serialize(output) : (value as null | undefined);
168+
options.sync?.[1](name, serialized);
169+
if (serialized != null) storage.setItem(name, serialized, storageOptions);
170+
else storage.removeItem(name, storageOptions);
171+
unchanged = false;
172+
return output;
173+
}
174+
: (...args: any[]) => {
175+
(signal[1] as any)(...args);
176+
const value = untrack(() => serialize(signal[0] as T));
177+
options.sync?.[1](name, value);
178+
storage.setItem(name, value, storageOptions);
179+
unchanged = false;
180+
},
181+
2: init,
182+
}) as PersistedState<S>;
192183
}
193184

194185
/**

packages/storage/test/persisted.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ describe("makePersisted", () => {
133133

134134
it("exposes the initial value as third part of the return tuple", () => {
135135
const anotherMockAsyncStorage = { ...mockAsyncStorage };
136-
const promise = Promise.resolve("init");
136+
const promise = Promise.resolve('"init"');
137137
anotherMockAsyncStorage.getItem = () => promise;
138138
const [_signal, _setSignal, init] = makePersisted(createSignal("default"), {
139139
storage: anotherMockAsyncStorage,

0 commit comments

Comments
 (0)