question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Initializing local state with an array stores incorrect value in the cache

See original GitHub issue

When writing data to the cache directly via cache.writeData, empty arrays are stored as follows:

cartItems: {"type":"json","json":[]}

This can be seen with Apollo’s Full Stack Tutorial in Section 8: Managing Local State.

Cause

This appears to be caused by apollo-client-inmemory’s behavior in writeFieldToStore:

https://github.com/apollographql/apollo-client/blob/dea8b34f722175871235bb424ac4d37cb28ec018/packages/apollo-cache-inmemory/src/writeToStore.ts#L273-L282

For local state, there will be no selectionSet and typeof [] === "object". Perhaps the Array.isArray() check should preceed !field.selectionSet || value === null?

Reproduction

  • Check out Apollo’s full stack tutorial from GitHub

  • cd to final/server

    • Install dependencies via npm ci and run the project npm start
  • In a second terminal, cd to final/client

    • Install dependencies via npm ci and run the project npm start
  • Load the browser, check the cache with Apollo Client DevTools

    image

Versions

This can be reproduced both with the latest stable releases and with the latest v2.5 release candidate (rc2, February 25th)

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:19
  • Comments:6 (1 by maintainers)

github_iconTop GitHub Comments

2reactions
veddermaticcommented, Sep 16, 2019

This behavior is especially troubling if the empty array is a child of an object in a collection. If the first child array is empty, objects in other children are stored a json, not as objects. This causes queries that reference those children to error.

Example:

If I have this schema:

type Parent {
  name: String!
  kids: [Child]!
}
type Child {
  name: String!
}

And I fetch all the parents, and store them in a stuff @client cache, it will look like this if the first parent returned has no children:

ROOT_QUERY
  stuff: [Parent]
    0: Parent: abc-123
      name: "A name"
      kids: {"type":"json","json":[]}
    1: Parent: def-456
      name: "Uh Oh:
      kids: {"type":"json","json":[{"id":"xyz-789","name":"A child","__typename":"Child"}]}

Any query that includes:

stuff @client {
  name
  kids {
    id
   name
 }
}

will error with Error: Network error: Can't find field name on object undefined. This is causing some serious issues in our app.

1reaction
Adam-Benocommented, Oct 3, 2019

After some digging I found out how to solve half of the problem. Since I’m using writeQuery to initialize the Parent object I had to pass along a gql which is where the problem was for the children array.

I was using this query to write in cache

gql`
  query InitParent{
    parents @client {
      id
      ...
      children
    }
  }
`;

to store following data:

{
  parents: [
    {
      id: 'someParentId',
      ...,
      kids: [],
    },
  ],
}

This as we already know from the posts above results in an error because the array is stored in cache like this:

kids: {"type":"json","json":[]}

However if you modify the query to include all the fields on the children object like this:

gql`
  query InitParent{
    parents @client {
      id
      ...
      children {
        id
        ...
        name
      }
    }
  }
`;

and use it with .writeQuery it will actually create an empty array instead of json:

kids: []

This is because apollo needs a selectionSet to be able to save it as an array rather than json.

But as I mentioned, this covers only half of the problem, because simple arrays such as:

kidNames: ['john', 'eva']

will still get stored as json:

kidNames: {
  type: json
  json: ['john', 'eva']
}
Read more comments on GitHub >

github_iconTop Results From Across the Web

Local State Management with Reactive Variables
For starters, here's how to initialize a default value for a name field on the Person type. const cache = new InMemoryCache({ typePolicies:...
Read more >
How to Initialize State for each Element in Array ReactJS
The solution for me was running the function in componentDidUpdate. My state now initializes correctly. Thanks again Junius for the help!
Read more >
Local Storage in React - Robin Wieruch
If there is no value in the local storage, we default to false for the initial state. The proposed solution works if local...
Read more >
State Management within React Functional Components with ...
We'll look at managing component state and local variables ... Your components can bind to the value of isAuthenticated. ... Return cached value...
Read more >
C++ Tutorial: Memory Allocation - 2020 - BogoToBogo
Parameters are local variables which are initialized with an assignment ... computers have three locations for storing data - physical memory, cache, ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found