urql: Using cache.updateQuery() data is null

urql version & exchanges:

image

Steps to reproduce

  1. go here: https://codesandbox.io/s/urql-svelte-crud-with-indexeddb-cqg5i?file=/urql.js:1152-1179
  2. click on “Todos list”
  3. click on a “Complete” button, the one you want
  4. it gives an error: Cannot set property 'text' of null

Why this error?

Why is data null?

I’m using this code:

import { initClient, dedupExchange, fetchExchange } from "@urql/svelte";
import { offlineExchange } from "@urql/exchange-graphcache";
import { makeDefaultStorage } from "@urql/exchange-graphcache/default-storage";
import gql from "graphql-tag";
import { TODOS_QUERY, TODO_QUERY } from "./gql";

const updates = {
  Mutation: {
    updateTodo: (result, args, cache, info) => {
      if (info.optimistic) {
        cache.updateQuery(
          { query: TODO_QUERY, variables: { id: info.variables.id } },
          data => {
            console.log("data:" + data);
            data.text = "UPDATING";
            return data;
          }
        );
      }
    }
  }
};

const optimistic = {
  updateTodo: () => {}
};

const storage = makeDefaultStorage({
  idbName: "graphcache-v3",
  maxAge: 7
});

const exchanges = [
  dedupExchange,
  offlineExchange({ storage, updates, optimistic }),
  fetchExchange
];

function OnInit() {
  initClient({ url: "https://h1pcl.sse.codesandbox.io", exchanges });
}

export default {
  OnInit
};

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Comments: 18 (10 by maintainers)

Most upvoted comments

Optimistic updates are meant to reflect an early answer from the server, using to indicate something is updating isn’t really its use case. This could be done by means of result.fetching instead.

The thing is, optimistic is used to ensure we can actually be offline, when a user is offline and performs a mutation just highlighting that certain thing is updating isn’t of much use to our user, if it’s unsafe to simulate an early response we should doubt that this entity can be mutated in an offline-state.

It’s the same as you are doing in updates.Mutation, cache.readFragment with the TodoFragment, id and you should have that data

There are no problems using the app, these are needed since they help a lot of people avoid pitfalls related to optimistic updates.

You can use the second argument to optimistic which is the cache to look for the missing fields

Also, to make this more complete, if your UI handles optional fields properly, it’s always possible to set these fields to null if they are indeed optional.

But the requirements of the optimistic mutation data will always have to match your selection set because that’s what will be written optimistically and emulate the server result.

Lastly, if those fields are undefined or missing, they’ll be skipped. So even seeing these warnings isn’t going to prevent you from defining the optimistic data. It’ll just likely be a mistake, since the query data won’t update fully. That’s why we warn about it. We could I suppose silence it if the field is explicitly set to undefined though

So what do you suggest for now?

I keep using cache.writeFragment?

Yep basically the thing we’re encouraging is atomic updates as often as possible, which increases consistency and reduces the amount of code that needs to be written or shared with the cache.

We’re also looking into a more lowlevel write primitive that is similar to cache.resolve but for writing rather than reading data fields.

Because you likely were using a resolver pattern, but we opted to not follow resolvers in write scenario’s to increase data consistency

First change here: https://github.com/FormidableLabs/urql/blob/master/exchanges/graphcache/CHANGELOG.md#patch-changes

Because you have never queried a TODO_QUERY yet at that point so for all the cache knows a TODO_QUERY might be returning something with __typename: Author it can’t assume anything about your data since that would lead to errors.

Your safest bet in that scenario is to utilize writeFragment which can safely assume this.

        cache.writeFragment(
          gql`
            fragment _ on Todo {
              id
              text
            }
          `,
          {
            id: args.id,
            text: "UPDATING"
          }
        );

I do wonder why you are doing an optimistic update there, you could safely do

optimistic: {
  updateTodo: (args) => ({ id: args.id, __typename: 'Todo', text: 'UPDATING' })
}