apollo-link-state icon indicating copy to clipboard operation
apollo-link-state copied to clipboard

Document refetch behavior

Open anthonator opened this issue 6 years ago • 8 comments

I think it would be very helpful to document how refetch behavior works with apollo-link-state. I lost a day with what I though was a bug in apollo-link-state. It didn't occur to me that refetch would set my state cache back to the defaults I set in withClientState.

It makes sense after putting all the pieces together. Unfortunately, it took me a day to solve this puzzle 😢

I imagine a lot of people write their queries similar to this:

const queries = gql`
  query {
    localQuery @client {
      someField
    }
    remoteQuery {
      someField
    }
  }
`;

export default graphql(queries)(MyComponent);

refetch-ing data in this scenario will reset localQuery to whatever is set in withClientState's defaults.

Developers will need to find a pattern that allows them to refetch their remote and local data independently.

Here's a pattern that I'm using. I don't know if it's any good but it's working for me for now.

const httpQueries = gql`
  query {
    remoteQuery {
      someField
    }
  }
`;

const stateQueries = gql`
  query {
    localQuery @client {
      someField
    }
  }
`;

export default compose(
  graphql(httpQueries, { name: 'httpData' }),
  graphql(stateQueries, { name: 'stateData' }),
)(MyComponent);

anthonator avatar Jul 02 '18 21:07 anthonator

I agree @anthonator -- I think the whole apollo-link-state defaults feature is a full of pitfalls. In our app, we're providing/testing our defaults inside of our state-link, and we don't use the Defaults feature at all.

fbartho avatar Jul 02 '18 22:07 fbartho

Same issue. It happens to me before when I wrote queries like this:

const queries = gql`
  query {
    localQuery @client {
      someField
    }
    remoteQuery {
      someField
    }
  }
`

Just like @anthonator mentioned above, refetch-ing data will reset the localQuery. Then I have to separate the localQuery from the remoteQuery.

Another issue is that when I mutate some fields of local state, the combined queries above won't get updated. Then again the seprated version works.

I think apollo-link-state is not good at dealing with combination of local and remote queries at the moment, or am I missing something about the goals apollo-link-state aims to acheive?

haipengz avatar Jul 26 '18 03:07 haipengz

I too ran into this and think it is unintuitive behavior, at least in how I originally interpreted the docs. It seems like, since fields with the @client directive resolve to the local cache instead of the network, that refetches to the network therefore should not affect them.

vpfaulkner avatar Sep 04 '18 17:09 vpfaulkner

Uups, that explains a few hours of my debugging too. Thanks @anthonator for writing this up!

barbalex avatar Sep 16 '18 14:09 barbalex

Is this behavior working as intended?

khell avatar Oct 08 '18 07:10 khell

Facing the same issue. Is there any solution to this problem ?

smitthakkar1 avatar Oct 11 '18 14:10 smitthakkar1

I specifically have this problem when trying to test a local mutation.

it("should update SearchText when the TextField is changed", async () => {
    let apolloClient
    const wrapper = mount(
      <MockedProvider mocks={mocks} addTypename={false}>
        <ApolloConsumer>
          {client => {
            apolloClient = client
            return <Search />
          }}
        </ApolloConsumer>
      </MockedProvider>
    )

    await wait(0)
    wrapper.update()

    // make sure search text is empty string on initial query
    const { data } = await apolloClient.query({ query: GetSearchRules })
    expect(data.searchRules.text).toBe("")

    // simulate an on change event on the text field, triggering a local
    // state mutation
    const event = { target: { value: "new search" } }
    const textField = wrapper.find("input")
    textField.simulate("change", event)

    await wait(0)
    wrapper.update()

    // check if getSearchText query returns correct string
    const { data: newData } = await apolloClient.query({
      query: GetSearchRules
    })

    // fails, because the query needs to be refetched, but cant refetch it
    // because it refetching local queries resets it to default state, which is null
    expect(newData.searchRules.text).toBe("new search")
  })

After I simulate the button click, a local state mutation fires which updates my state. When I make the second query, it fails because the local-state query needs to be refetch, but that causes the local state to be reset.

In my <Search/> component, if I add refetchQueries={[{ query: GetSearchRules }]} to the <Mutation/> component prop, my tests pass, but the UI is broken. If I remove refetchQueries, tests fail, but UI works as desired.

ramsaylanier avatar Oct 25 '18 21:10 ramsaylanier

I've run into it as well

norbertkeri avatar Jan 16 '19 12:01 norbertkeri