'The normalized cache in this REPRO is not updating an entity field, why?
The normalized caching in this REPRO with urql and svelte is not updating an entity field.
Steps:
- Go to https://stackblitz.com/edit/sveltejs-kit-template-default-6tzo2i
- open a new terminal with the
+icon - start apollo-server with:
cd server && npm i && node . - Go to "Teams" page
- click on the first Team ID
- on the team page the players list is shown
- obviously the player's name is
undefinedbecause in that query I don't ask for it - click on the first player button
- the query is correctly providing the
TeamPlayerwith ID0and it'snamebuturqldoesn't update the playernamewhich is stillundefined
QUESTION
Why is the player name not updated after the manual-with-button query?
GIF


Solution 1:[1]
Why would it get updated?
<script lang="ts">
import { gql, operationStore, query } from "@urql/svelte";
export let player;
const TeamPlayerByIDQuery = gql`
query teamPlayerByID($id: ID!) {
teamPlayerByID(id: $id) {
id
name
}
}
`;
const teamPlayerByIDStore = operationStore(
TeamPlayerByIDQuery,
{ id: player?.id },
{ requestPolicy: "cache-and-network", pause: true }
);
query(teamPlayerByIDStore);
function queryTeamPlayer() {
// teamPlayerByIDStore.variables = { id: player.id }; // this is superfluous
teamPlayerByIDStore.reexecute({ ...teamPlayerByIDStore.context, pause: false });
}
</script>
<li>player ID: {player?.id} - name: {player?.name} <button type="button" on:click={queryTeamPlayer}>Query player from network</button> {JSON.stringify($teamPlayerByIDStore)}</li>
You're asking about {player?.name}, but in this components there's nowhere where the variable is reassigned, so nothing Svelte would see. As it is, it is never mutated at all, nor is the whole reference to the player object passed to another function that could mutate it out of sight (all of which Svelte wouldn't know of, since it's not a store either).
So, to sum it up: this variable never changes in this component.
The only possible source of change left is if the value of the prop changes (since it is a let export).
Let's see the parent component that creates this one and provides this prop...
First the whole code, so that future readers can get context even if the Stackblitz isn't available anymore, then we'll extract the relevant pieces:
<script lang="ts">
import { gql, operationStore, query } from "@urql/svelte";
import { page } from "$app/stores";
import { client } from "$lib/urql";
import Player from "./_Player.svelte";
const TeamByIDQuery = gql`
query teamByID($id: ID!) {
teamByID(id: $id) {
id
description
players {
id
name
}
}
}
`;
const teamByIDStore = operationStore(
TeamByIDQuery,
{ id: $page.params.id },
{ requestPolicy: "cache-and-network" }
);
query(teamByIDStore);
$: team = $teamByIDStore.data?.teamByID
</script>
<h2>Team ID: {team?.id}</h2>
<br>
description: {team?.description}
<br><br>
<h3>Team players</h3>
<pre>{JSON.stringify(team)}</pre>
<ul>
{#each team?.players || [] as player}
<Player {player}></Player>
{/each}
</ul>
<br><br>
$teamByIDStore: {JSON.stringify($teamByIDStore)}
So, here's our Player:
{#each team?.players || [] as player}
<Player {player}></Player>
{/each}
That comes from this team:
$: team = $teamByIDStore.data?.teamByID
So, basically, our player in the other component is:
$teamByIDStore.data?.teamByID?.players[i] // for each i of players
teamByIDStore is a store and is reactive, indeed:
const teamByIDStore = operationStore(
TeamByIDQuery,
{ id: $page.params.id },
{ requestPolicy: "cache-and-network" }
);
It happens to be fed by the following GraphQL query:
const TeamByIDQuery = gql`
query teamByID($id: ID!) {
teamByID(id: $id) {
id
description
players {
id
}
}
}
`;
As demonstrated above, this is the only source of data that ends up feeding the player in the above component. And it's not selecting the name of the player.
That's why the name of the player remains undefined: it is not fetched by the GraphQL query that feeds it.
If you were to add name to said query:
players {
id
name
}
... then the name of the player in the Player component wouldn't be undefined anymore... But it would still not come from the query in the Player component. It would be there before you press the button.
I don't know much about @svelte/urlq but I doubt it does any arcane merging of your separate GraphQL queries... It might do magic behind the scene with caches and all but, still, you only get what you're asking for in the results of a given query. I guess. Would make sense.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | rixo |
