T
TanStack3y ago
optimistic-gold

Persister writing every 1 second

Hello, I just noticed that my persister is writing every one second with the only difference being timestamp. I narrowed it down to a "DigitalClock" component that I have:
export default function DigitalClock({ id }: Props) {
const { beautifulDashboardQuery } = useBeautifulDashboardStorage({ id });

const {
clock: { showSeconds },
} = beautifulDashboardQuery.data;
const [time, setTime] = useState(new Date());
const [period, setPeriod] = useState(getPeriod());

useInterval(() => {
setTime(new Date());
}, 1000);

const formattedTime = formatTime(time, showSeconds);

useInterval(() => {
setPeriod(getPeriod());
}, 60000);

return (
<span className="text-8xl font-black tabular-nums">
{formattedTime}
<span className="text-5xl tracking-wider">{period}</span>
</span>
);
}
export default function DigitalClock({ id }: Props) {
const { beautifulDashboardQuery } = useBeautifulDashboardStorage({ id });

const {
clock: { showSeconds },
} = beautifulDashboardQuery.data;
const [time, setTime] = useState(new Date());
const [period, setPeriod] = useState(getPeriod());

useInterval(() => {
setTime(new Date());
}, 1000);

const formattedTime = formatTime(time, showSeconds);

useInterval(() => {
setPeriod(getPeriod());
}, 60000);

return (
<span className="text-8xl font-black tabular-nums">
{formattedTime}
<span className="text-5xl tracking-wider">{period}</span>
</span>
);
}
The 1 second cadence makes sense, but what doesnt make sense is why react-query tries to write to the cache every time. If I comment out the component then react query stops writing every second!
9 Replies
optimistic-gold
optimistic-goldOP3y ago
Let me give a bit more code/clarity
export function useBeautifulDashboardStorage({ id }: { id: string }) {
const KEY = ['storage', 'dashboards', 'beautiful', id];
const { query, mutation } = useBuildHooks({
queryKey: KEY,
dataStore,
});

if (!query.data) throw new Error(`Suspense not called for ${KEY.join('.')}}`);

return {
beautifulDashboardQuery: query,
beautifulDashboardMutation: mutation,
};
}
export function useBeautifulDashboardStorage({ id }: { id: string }) {
const KEY = ['storage', 'dashboards', 'beautiful', id];
const { query, mutation } = useBuildHooks({
queryKey: KEY,
dataStore,
});

if (!query.data) throw new Error(`Suspense not called for ${KEY.join('.')}}`);

return {
beautifulDashboardQuery: query,
beautifulDashboardMutation: mutation,
};
}
and the query from useBuildHooks looks something like this:
const query = useQuery({
queryKey,
queryFn: async () => {
const store = await chromeLocalStorage.get<Data<T>>(storageKey);
const data = normalizeData(store);
version.current = data.version;
return data.data;
},
suspense: true,
meta: {
storable: false,
},
...queryOptions,
});
const query = useQuery({
queryKey,
queryFn: async () => {
const store = await chromeLocalStorage.get<Data<T>>(storageKey);
const data = normalizeData(store);
version.current = data.version;
return data.data;
},
suspense: true,
meta: {
storable: false,
},
...queryOptions,
});
const asyncStoragePersister = createAsyncStoragePersister({
throttleTime: 0,
storage: {
async getItem(key) {
return JSON.stringify(await chromeLocalStorage.get(key));
},
async removeItem(key) {
return chromeLocalStorage.remove(key);
},
async setItem(key, value) {
console.log('setting', key, value);
await chromeLocalStorage.set(key, JSON.parse(value));
},
},
});

const persistOptions: Omit<PersistQueryClientOptions, 'queryClient'> = {
persister: asyncStoragePersister,
dehydrateOptions: {
shouldDehydrateQuery: (query) => {
const isValid = query.state.status === 'success';
const isStorable = (query.meta?.storable as boolean) ?? true;
return isValid && isStorable;
},
},
};

const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
cacheTime: ms('1 day'),
},
},
});
const asyncStoragePersister = createAsyncStoragePersister({
throttleTime: 0,
storage: {
async getItem(key) {
return JSON.stringify(await chromeLocalStorage.get(key));
},
async removeItem(key) {
return chromeLocalStorage.remove(key);
},
async setItem(key, value) {
console.log('setting', key, value);
await chromeLocalStorage.set(key, JSON.parse(value));
},
},
});

const persistOptions: Omit<PersistQueryClientOptions, 'queryClient'> = {
persister: asyncStoragePersister,
dehydrateOptions: {
shouldDehydrateQuery: (query) => {
const isValid = query.state.status === 'success';
const isStorable = (query.meta?.storable as boolean) ?? true;
return isValid && isStorable;
},
},
};

const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
cacheTime: ms('1 day'),
},
},
});
I was thinking maybe because setTime causes a re-render which causes the useQuery hook to re-evaluate, but I also don't think that should be the case
genetic-orange
genetic-orange3y ago
it's because observer options are updated. We have a PR to ignore these events
complex-teal
complex-teal3y ago
Looks like setTime’s rerender to me
genetic-orange
genetic-orange3y ago
GitHub
perf(persist): subscriber calls persistQueryClientStore only on cac...
By default persistQueryClientSubscribe was calling persistQueryClientSave on every single change in query cache. It was also triggered by events like observerOptionsUpdated which are emitted very o...
rare-sapphire
rare-sapphire3y ago
This ☝️ we're working on it 🙂
optimistic-gold
optimistic-goldOP3y ago
Awesome, thanks so much!! Any update on this? Seems like the tests are missing
stormy-gold
stormy-gold3y ago
I should add missing test this week hopefully, sorry for the delay In the meantime I’ll share with you a snippet which you can safely use to set it up by yourself when I’m at my desk Maybe you can find sth helpful in this thread: https://discord.com/channels/719702312431386674/1067399193255231499/1068138769808629811 @TkDodo 🔮, @Louis I just added missing test: https://github.com/TanStack/query/pull/4884/commits/2b44c28bf872048d2180f9df8b2e5a1ff0e36bf5 Sorry for the delay!
genetic-orange
genetic-orange3y ago
thanks for this - great job 👏 . I just merged the PR 🚀
stormy-gold
stormy-gold3y ago
Aaaawsome! 🥳

Did you find this page helpful?