solid-query: EFFECT_RELAY_TEAR on Solid 2 when a query key changes — useBaseQuery relays the result through a version signal written from a render effect
Maintainer antworten meist innerhalb von 1 Tag
Bewertung
- Schwierigkeit
- 4/5
- Geschätzter Aufwand
- 3-5 Tage
- Anfängerfreundlichkeit
- 45/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- typescript
- Bereich
- frontend
Rechercherichtung
The adapter's useBaseQuery (the payload points at build/index.js) wires createRenderEffect(() => defaultedOptions(), opts => observer.setOptions(opts)), then relays cache events into a version signal read by query. Start by reproducing the key switch with the issue's captureArtifact snippet under vitest/jsdom and confirming EFFECT_RELAY_TEAR. Trace whether the observer result can be derived as a memo or projection of the current options instead of a counter written from onCacheEvent; done means the diagnostic no longer fires and query.data shows the new key's value in the same flush.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Describe the bug
With Solid 2 ([email protected]) and @tanstack/[email protected] (the useBaseQuery code is identical in 6.0.0-rc.5), switching the query key of a useQuery logs a reactivity diagnostic from @solidjs/[email protected]:
[EFFECT_RELAY_TEAR] memo "computed" ran twice for one write of "tariff": once in the flush where "tariff" changed,
and again after effect "effect" relayed it by writing "signal" — the first frame showed the new "tariff" with the stale "signal".
If "signal" is computed from what the effect reads, make it a memo so readers get it in the same flush;
if the write reads something outside the graph (layout, time), the tear is the cost of measuring.
Owner path: <QueryClientProvider> › <provider> › computed › <Price> › computed — the only user code is a createMemo reading query.data. One diagnostic per useQuery instance whose key changed (a screen with five queries logs five).
Solid 2 dev builds treat these diagnostics as actionable (the console is expected to stay clean), so every app that derives anything from query.data and ever changes a key gets a warning it cannot fix on its side.
Where it comes from
useBaseQuery (build/index.js):
createRenderEffect(
() => defaultedOptions(),
(opts) => observer.setOptions(opts)
);
const [version, setVersion] = createSignal(0, { ownedWrite: true });
const onCacheEvent = (event) => {
if (event.query.queryHash === untrack(defaultedOptions).queryHash || event.query.queryHash === latestHash) {
setVersion((v) => v + 1);
}
};
...
const query = () => {
version();
return lookupQuery();
};
When the key changes, defaultedOptions is recomputed in the same flush as the key signal; readers of query.data see the new options with the old observer result, then the render effect calls observer.setOptions, the cache event bumps version, and every memo downstream recomputes a second time. That is exactly the "effect relays a derived value by writing a signal" pattern the diagnostic describes: version is derived from what the effect reads, so the first frame is torn.
Your minimal, reproducible example
import { captureArtifact } from '@solidjs/diagnostics';
import { render } from '@solidjs/testing-library';
import { QueryClient, QueryClientProvider, useQuery } from '@tanstack/solid-query';
import { createMemo, createSignal, flush } from 'solid-js';
function Price(props: { tariff: string }) {
const query = useQuery(() => ({ queryKey: ['price', props.tariff], queryFn: async () => props.tariff.length }));
const doubled = createMemo(() => (query.data ?? 0) * 2);
return <span>{doubled()}</span>;
}
const client = new QueryClient({ defaultOptions: { queries: { staleTime: Infinity, retry: false } } });
client.setQueryData(['price', 'base'], 1);
client.setQueryData(['price', 'site'], 2);
const [tariff, setTariff] = createSignal('base');
const { artifact } = await captureArtifact(async () => {
const screen = render(() => (
<QueryClientProvider client={client}>
<Price tariff={tariff()} />
</QueryClientProvider>
));
flush();
setTariff('site'); // key change
flush();
await new Promise((r) => setTimeout(r, 30));
flush();
screen.unmount();
}, { scenario: 'useQuery key switch', attribution: true });
console.log(artifact.diagnostics.map((e) => e.code)); // ['EFFECT_RELAY_TEAR']
Both keys are already in the cache, so no fetch is involved — the second recompute is purely the version relay.
Steps to reproduce
- Mount a component with
useQuerywhose key depends on a signal, and acreateMemoreadingquery.data. - Change the signal (with
@solidjs/diagnosticscapturing, attribution on). - Observe
EFFECT_RELAY_TEARfor the memo.
Expected behavior
Changing a query key should produce one consistent frame: the result for the new key arrives in the same flush as the key (e.g. the observer result derived as a memo / projection of the options instead of being relayed through a counter signal written from a cache subscription), or the diagnostic should not fire for useQuery consumers.
Platform
- OS: macOS 13
- Browser: Brave (Chromium), also reproduces under vitest + jsdom
[email protected],@solidjs/[email protected]@tanstack/[email protected](sameuseBaseQueryin rc.5)
TanStack Query adapter
solid-query
TanStack Query version
6.0.0-rc.4 / 6.0.0-rc.5
TypeScript version
7.0
Additional context
Related: #11358 (STRICT_READ_UNTRACKED in the same adapter under Solid 2 diagnostics).
- Vorherrschende Sprache
- TypeScript
- Sterne
- 50.4k
- Forks
- 4.3k
- Ø Merge
- 11 Std. 37 Min.
- Gemergte PRs (30 T.)
- 521
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus TanStack/query
-
QueryClient.isFetching and useIsFetching accept a fetchStatus filter and ignore itEvtl. vergeben @Apu-Emdad hat das vor 5 Tagen übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 1 Tag
-
solid-query: STRICT_READ_UNTRACKED on Solid 2 — client()/options() read in component body (useMutation, useBaseQuery)Evtl. wieder frei @LeonxLJX hat das vor 46 Tagen übernommen, und es ist kein Pull Request offen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
TanStack/query#11358 · 2 Kommentare · 1 Reaktion ·
Maintainer antworten meist innerhalb von 1 Tag
-
solid-query 6.0.0-rc.5: onCacheEvent throws NotReadyError during a Solid 2 (rc.14+) transition landingEvtl. vergeben @UzumakiODZ hat das vor 5 Tagen übernommen. Offen
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 28/100
TanStack/query#12031 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
Explicit gcTime schedules a GC timer during SSR, retaining the whole render in memoryEvtl. vergeben @AdzerKI hat das vor 5 Tagen übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 25/100
TanStack/query#12025 · 2 Kommentare ·
Maintainer antworten meist innerhalb von 1 Tag
-
restoreQueries and persisterGc throw on malformed persisted entriesEvtl. vergeben @VGontier-cmd hat das vor 14 Tagen übernommen. Offen
Schwierigkeit 3/5 1-2 Tage Anfängerfreundlichkeit 72/100
Maintainer antworten meist innerhalb von 1 Tag
Ähnliche Issues
-
enhancement
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
equinor/prisma-decision-web#919 ·
Maintainer antworten meist innerhalb von 1 Tag
-
[DC] OneLogin sync imports a partial user list when an API request failsEvtl. vergeben @RaphaelFakhri hat das heute übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 84/100
bitwarden/directory-connector#1276 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 62/100
home-assistant/installer#324 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Mondriaan
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 65/100
knaw-huc/textannoviz#733 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
Maintainer antworten meist innerhalb von 1 Tag