React-apollo: [@apollo/react-hooks] can `data` be `undefined`?

Created on 27 Jun 2019  ·  8Comments  ·  Source: apollographql/react-apollo

Intended outcome:

I'm using @apollo/react-hooks#useQuery and I noticed sometimes data can be set to undefined. I would expect it to always return an object, even if empty during loading or in case of errors.

Actual outcome:

data can be undefined, I don't understand if this is expected?

How to reproduce the issue:

const { data } = useQuery(QUERY);

if (data === undefined)
  throw new Error('found undefined data');
}

Version

@apollo/[email protected]

confirmed

Most helpful comment

Thanks for the answer, I notice most often than not, data is set to an empty object though... Is this expected?

Anyways, this can be easily tweaked by doing:

const { data = {} } = useQuery(QUERY);

to say that it's not a big issue, and may actually be a good thing so that people who prefer to get undefined have it, and people who prefer an empty object can easily achieve that with 3 additional characters.

All 8 comments

This has been a topic of discussion before, but as far as I remember it was decided to not return an empty object by default as this would be a pretty big breaking change and cause quite some issues for existing code, I'm not sure if this behavior is changing now with 3.0. Also keep in mind that when a query errors you will most likely get undefined as well.

The docs & types also reflect this:

data: TData
An object containing the result of your GraphQL query. Defaults to undefined.

edit: Also see #2424

Thanks for the answer, I notice most often than not, data is set to an empty object though... Is this expected?

Anyways, this can be easily tweaked by doing:

const { data = {} } = useQuery(QUERY);

to say that it's not a big issue, and may actually be a good thing so that people who prefer to get undefined have it, and people who prefer an empty object can easily achieve that with 3 additional characters.

I'm not sure, I think the behavior is pretty inconsistent and depends on errors, cache, etc. I'm always checking whether data exists before accessing as we've been bitten by it before and I'd suggest you do the same :)

Thanks for reporting this @FezVrasta. Yes, this is a bit annoying for sure; we need to align this across the board.

Okay, here's how things are currently setup in master (not all of this is available in the current beta but will be shortly):

  • When results are loading, data is set to {}. Technically speaking undefined is more correct (since the data doesn't exist yet), but setting data to {} does help avoid extra data is truthy checks (the approach is a little more developer friendly). A good way to look at the initial {} is that it's the result of useQuery getting the data ready to hold values (it's prepping data as a container).

  • If an error occurs and a last data result is available, the last data result is re-used. If the last data result isn't available, data is undefined. We don't want to set data to {} here, as we're not getting data ready to hold results; an error has occurred and we have no data, so undefined seems like a better fit.

  • If the skip option is set to true, data is undefined. So again since we're not prepping data to hold a result (since we're skipping running the query), undefined seems like a better choice.

  • The initial data value when using SSR has been aligned to match the client side equivalent. So it will be {} when loading on the server as well.

The above is the current approach we're planning on launching with React Apollo 3. While not perfect, it seems to be the best way to meet in the middle of developer usability and correctness. @FezVrasta the above means that the example you provided earlier in this issue thread would never throw the error, as data would be {}.

If anyone objects to this approach definitely let me know. Thanks!

@hwillson I was just trying out the Apollo hooks package for the first time while migrating away from graphql-hooks. I tried urql before moving on to Apollo.
Both other clients return undefined as data before the first successful fetch.
I was quite confused by this. I'm using TypeScript and now I have to check if data is there, because it's defined as | undefined, then I have to check my top-level object of my query projects if it's there. For the case data is {}.
Also in that case that data is just {}, the typings fail.
Basically it enforces me to make sure data isn't undefined but it just accepts data.projects to be there which isn't true for this initial state. Now I have to remember as a developer to check that because I'm getting runtime errors.
That all is quite unintuitive. Any suggestions how to use the Apollo hooks with TypeScript?

@hwillson It's okay for data to be {} while the operation is in progress however I would much rather have data stay {}. In fact, compared the the current combination, I'd also prefer if data were undefined in both cases. The added consistency makes the whole concept easier to grasp and explain to others.

P.S. Adding an initial value for the data might encompass the best of both worlds

@mateja176 We're changing things around so data is always undefined where there is no data. See https://github.com/apollographql/react-apollo/pull/3388.

Was this page helpful?
0 / 5 - 0 ratings