Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue with writeData() and nested array fields #4785

Closed
mbrowne opened this issue May 7, 2019 · 4 comments
Closed

Issue with writeData() and nested array fields #4785

mbrowne opened this issue May 7, 2019 · 4 comments
Assignees

Comments

@mbrowne
Copy link

mbrowne commented May 7, 2019

In some conditions involving queries with nested arrays, client.writeData() doesn't work, causing subsequent queries to fail.

Intended outcome:

The following demo should work without errors:
https://github.com/mbrowne/react-apollo-error-template/tree/cache-array-issue

Actual outcome:

The client.writeData() apparently fails to update the cache correctly. The next time the component renders, no data is returned from the query:

TypeError: Cannot read property 'results' of undefined

How to reproduce the issue:

Clone the cache-array-issue branch of my demo repo:
git clone git@github.com:mbrowne/react-apollo-error-template.git --branch cache-array-issue --single-branch

It seems that this issue only happens when querying a nested array field more than one level deep. For example, if you comment out the favoriteColors field in my demo, there are no errors and everything works as expected:

const ALL_PEOPLE = gql`
  query AllPeople {
    people {
      results {
        id
        name
        friends {
          id
          name
          # favoriteColors {
          #   id
          #   label
          # }
        }
      }
      totalCount
    }
  }
`

It also only happens with particular data...for example, in my demo data, 'John Smith' has the following friends:

friendIds: [2, 3]

If you simply reverse the order to:

friendIds: [3, 2]

...then it works. When I was debugging this, it seemed to have something to do with the graphql query that writeData generates automatically behind the scenes (via the fragmentFromPojo helper). Since fragmentFromPojo cannot determine the nested fields from an empty array, it generates a query that doesn't include the necessary fields, e.g. just favoriteColors instead of:

favoriteColors {
  id
  label
}

Versions

  System:
    OS: macOS 10.14.4
  Binaries:
    Node: 10.10.0 - /usr/local/bin/node
    Yarn: 1.15.2 - /usr/local/bin/yarn
    npm: 6.4.1 - /usr/local/bin/npm
  Browsers:
    Chrome: 74.0.3729.131
    Firefox: 66.0.3
    Safari: 12.1
  npmPackages:
    apollo-cache-inmemory: 1.6.0-rc.1 => 1.6.0-rc.1
    apollo-client: ^2.5.1 => 2.5.1
    apollo-link: ^1.2.11 => 1.2.11
    apollo-link-http: ^1.5.14 => 1.5.14
    react-apollo: 2.5.3 => 2.5.3
@benjamn benjamn self-assigned this May 7, 2019
@mbrowne
Copy link
Author

mbrowne commented May 7, 2019

I tracked down the issue...selectionSetFromObj() always passes the first element of every array in order to determine the fields in the array elements (https://github.com/apollographql/apollo-client/blob/master/packages/apollo-cache/src/utils.ts#L67). But this doesn't work when the array is empty. For example, given this data:

  friends: [
    { id: '2', name: 'Sara Smith', favoriteColors: [], __typename: 'Person' },
    {
      id: '3',
      name: 'Budd Deey',
      favoriteColors: [
        { id: 'magenta', label: 'Magenta', __typename: 'Color' }
      ],
      __typename: 'Person'
    }
  ]

The favoriteColors array for Sara Smith is empty, so selectionSetFromObj won't determine that the Color id and label need to be selected (which it would have known if it had looked at { id: 'magenta', label: 'Magenta', __typename: 'Color' }).

I was thinking about working on a PR for this, but I'm not sure of the best solution here. For one thing, I'm not sure how smart writeData is supposed to be...the easiest solution would simply be to throw a warning if any empty arrays are found, saying that using writeFragment would be more reliable. A more robust fix would involve more complexity and looping/recursion (and more code of course).

@jounii
Copy link

jounii commented May 16, 2019

I stumbled across similar issue with writeData today regards storing object with nested object data. The writeData utils generate query with all keys nested in the query, so it will obviously write the data such way that it requires using the id and __typename in the nested objects which in turn are cached separately in flat cache data.

However with custom writeQuery with no field nesting, the nested objects can be saved and queried without the id and __typename requirement as cache stores the data as "type: 'json'" objects as it makes the fields work as values (although objects) and not as graphql objects.

I guess it's intended functionality, but was a bit gotcha.

@jack-sf
Copy link

jack-sf commented Jun 5, 2019

We should probably add a note to apollo documentation about the usage of writeData and quirks like the above one mentioned by @jounii . I wanted to add an object directly to cache with writeData and only after a bit of time I found this issue and learned that I can't do it, if the object consists of nested objects (that are stored as json values).

@hwillson
Copy link
Member

writeData has been removed from Apollo Client (for the full details, see #5923). Thanks!

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Feb 16, 2023
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants