joshnuss / svelte-persisted-store

A Svelte store that persists to localStorage

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

`persisted` throws when getting value `undefined` (because of `JSON.parse(undefined))`)

acedeb opened this issue · comments

Current Behavior

Initially posted here: skeletonlabs/skeleton#2266

When initializing or setting the persisted with undefined the store throws an error when trying to parse undefined, and the site is not rendered.

// lib/stores.ts
export const idStore: Writable<string | undefined> = localStorageStore(
  "some-id",
  undefined,
);
// routes/+page.svelte
<script>
	import { idStore } from '$lib/stores'

	idStore.set(undefined);
	console.log(`idStore: ${$idStore}`);
</script>

Expected Behavior

Would expect the store to return undefined. This works correctly when using e.g. null instead, as JSON.parse(null) returns null.

Solution

I'm a bit unsure what the best solution is, as I don't think we can differentiate between a getting undefined or getting a string "undefined". AFAIK, when setting undefined this become the string "undefined".
I can think of 2 solutions (if its not possible to differentiate between "undefined" and undefined):

  1. We need to check it the variable json is equal to the string "undefined" where we would return undefined instead. This, however, will cause a bug when someone tries to get the string "undefined", but gets undefined instead, so not good.
  2. We ban undefined in the generic type parameter T, which forces people to use null instead. I think I prefer this.

Link to Reproduction / Stackblitz

https://stackblitz.com/edit/sveltejs-kit-template-default-4uen9v?file=src%2Froutes%2F%2Bpage.svelte

If you'd like to use data types that are unsupported by JSON.stringify()/JSON.parse(), switch the serializer to devalue.

Here is an example from the Readme:

import * as devalue from 'devalue'

// third parameter is options.
export const preferences = persisted('local-storage-key', 'default-value', {
  serializer: devalue, // defaults to `JSON`
})

Let me know if that resolves the issue for you.

Thank you for answering.

Yes, this solves the issue. In my case, I can also use null instead of undefined, but I usually just default to undefined.

I do think though, that in general it would be nice to not having to know the internals of the library, i.e. that persisted is using JSON underneath, for me to know that undefined is not supported (by default), and will throw. Since undefined is working when using svelte writable, my (smooth) brain assumed that it would work here as well (though one can argue that of course it would be using JSON parser since we are retrieving the value from localstorage).

I don't know really know what you could change though in this case, so closing this.

'devalue' seems very interesting - thanks for pointing me to that as well.