Skip to content

PersistedState.current causes all reactive dependencies to rerender even if the value hasn't changed. #382

@niemyjski

Description

@niemyjski

Describe the bug

I have tanstack query and a few things looking at the current persisted local storage value. it's looking directly at current and has a custom serializer. I noticed that every read triggers all $effects and reactive properties to rerender due to subscribe and returning a new proxy even though the value didn't change. Why do we need this code when we can just listen for localstorage events for changes? I'd think this value would be cached, this is causing tens of thousands of extra requests and logic in my app.

Reproduction

using latest version.

get current() {
    this.#subscribe?.();  // ← THIS IS THE PROBLEM!
    const storageItem = this.#storage?.getItem(this.#key);
    const root = storageItem ? this.#deserialize(storageItem) : this.#current;
    return proxy(root, root, this.#proxies, this.#subscribe?.bind(this), this.#update?.bind(this), this.#serialize.bind(this));
}
export const accessToken = new PersistedState<null | string>('satellizer_token', null, { serializer: authSerializer });


export function getOrganizationUsersQuery(request: GetOrganizationUsersRequest) {
    const queryClient = useQueryClient();

    return createQuery<FetchClientResponse<ViewUser[]>, ProblemDetails>(() => ({
        enabled: () => !!accessToken.current && !!request.route.organizationId,
        onSuccess: (data: FetchClientResponse<ViewUser[]>) => {
            data.data?.forEach((user) => {
                queryClient.setQueryData(queryKeys.id(user.id!), user);
            });
        },
        queryClient,
        queryFn: async ({ signal }: { signal: AbortSignal }) => {
            const client = useFetchClient();
            const response = await client.getJSON<ViewUser[]>(`organizations/${request.route.organizationId}/users`, {
                params: {
                    ...request.params,
                    limit: request.params?.limit ?? 1000
                },
                signal
            });

            return response;
        },
        queryKey: [...queryKeys.organization(request.route.organizationId), { params: request.params }]
    }));
}

Every time it checks to see if this tanstack query can run, it's mutating the value and retriggering all queries and anything on the page.

Logs

api.svelte.ts:284 api: organizations/api.svelte.ts: getOrganizationsQuery enabled check {enabled: true, hasToken: true}
api.svelte.ts:284 api: organizations/api.svelte.ts: getOrganizationsQuery enabled check {enabled: true, hasToken: true}
api.svelte.ts:284 api: organizations/api.svelte.ts: getOrganizationsQuery enabled check {enabled: true, hasToken: true}
api.svelte.ts:284 api: organizations/api.svelte.ts: getOrganizationsQuery enabled check {enabled: true, hasToken: true}
api.svelte.ts:284 api: organizations/api.svelte.ts: getOrganizationsQuery enabled check {enabled: true, hasToken: true}
api.svelte.ts:299 api: organizations/api.svelte.ts: getOrganizationsQuery fetching {params: undefined}

System Info

System:
    OS: macOS 26.1
    CPU: (16) arm64 Apple M4 Max
    Memory: 2.04 GB / 64.00 GB
    Shell: 5.9 - /bin/zsh
  Binaries:
    Node: 25.1.0 - /opt/homebrew/bin/node
    npm: 11.6.2 - /opt/homebrew/bin/npm
    pnpm: 10.18.3 - /opt/homebrew/bin/pnpm
    Deno: 2.5.6 - /opt/homebrew/bin/deno
  Browsers:
    Chrome: 142.0.7444.60
    Firefox: 144.0.2
    Safari: 26.1

Severity

blocking all usage of runed

Metadata

Metadata

Assignees

No one assigned

    Labels

    triagePending triage by a maintainer to give it proper labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions