Etienne
Etienne

Reputation: 177

ApolloConsumer and cache

I'm currently using React and Graphql with Apollo and react-apollo. When I want to load data from Graphql, I normally use the < Query > component from react-apollo, but if I want to make a call after a event like a click, as far as I know, I need to do it with the ApolloConsumer and client.query.

But by doing it like that, when the cache is updated or resetted, everything that was loaded via ApolloConsumer is not updated. Here is an example:

import React, { Component } from "react";
import { render } from "react-dom";

import ApolloClient from "apollo-boost";
import { ApolloProvider, ApolloConsumer, Query } from "react-apollo";
import gql from "graphql-tag";

const client = new ApolloClient({
  uri: `https://m08pj5j9k9.lp.gql.zone/graphql`
});

class App extends Component {
  state = { client: [] };

  renderQuery = () => {
    return (
      <Query query={gql`{test01 {id, name}}`}>
        {({ loading, error, data }) => {
          if (loading) return "Loading...";
          if (error) return "Error";

          return (
            <ul>
              {data.test01.map(data => <li key={data.id}>{data.name}</li>)}
            </ul>
          );
        }}
      </Query>
    );
  };

  btnQueryClick = async client => {
    const data = await client.query({ query: gql`{test02 {id, name}}` });
    this.setState({ client: data.data.test02 });
  };

  render() {
    return (
      <ApolloProvider client={client}>
        <div>
          <h2>Query test</h2>
          {this.renderQuery()}

          <ApolloConsumer>
            {client => (
              <div>
                <h2>Client query test</h2>
                <button onClick={() => this.btnQueryClick(client)}>
                  Test!
                </button>
                <ul>
                  {this.state.client.map(data => (
                    <li key={data.id}>{data.name}</li>
                  ))}
                </ul>

                <h2>Store reset</h2>
                <button
                  onClick={() => {
                    client.resetStore();
                  }}
                >
                  {" "}
                  Reset!
                </button>
              </div>
            )}
          </ApolloConsumer>
        </div>
      </ApolloProvider>
    );
  }
}

render(<App />, document.getElementById("root"));

And here is a link to try it live: https://codesandbox.io/s/5460952y3k

On all requests, I padded a random number, so I know if the data has changed. As you can see, the data in the "Query test" section will be updated if I press the reset button, but not the data in the "Client query test" section.

Is there a way to emulate the same kind of updates with ApolloConsumer?

Thank you

Upvotes: 1

Views: 2971

Answers (2)

xadm
xadm

Reputation: 8418

You can't expect automatic updates of manually fired queries.

Even rerendered (if it happened, but it doesn't) consumer won't 'refire' event then no requeried, no state (and view) update.

Query doesn't create any kind of observable - you're probably looking for mixing manual queries with subscriptions.

Why do you need to watch for other cache updates?

Try to use 'traditional' apollo HOCs and compose - IMHO much more readable, manageable and suitable for more complex sceanrios.

Upvotes: 1

TLadd
TLadd

Reputation: 6894

Yeah, I think for your specific use case, client.query probably isn't the right fit. Here is a codepen that has what I think you're looking for: https://codesandbox.io/s/yw779qx8qv

And the source:

import React, { Component } from "react";
import { render } from "react-dom";

import ApolloClient from "apollo-boost";
import { ApolloProvider, ApolloConsumer, Query } from "react-apollo";
import gql from "graphql-tag";

const client = new ApolloClient({
  uri: `https://m08pj5j9k9.lp.gql.zone/graphql`
});

class App extends Component {
  state = { showOtherQuery: false };

  renderQuery = () => {
    return (
      <Query query={gql`{test01 {id, name}}`}>
        {({ loading, error, data }) => {
          if (loading) return "Loading...";
          if (error) return "Error";

          return (
            <ul>
              {data.test01.map(data => <li key={data.id}>{data.name}</li>)}
            </ul>
          );
        }}
      </Query>
    );
  };

  renderOtherQuery() {
    return (
      <Query query={gql`{test02 {id, name}}`}>
        {({ loading, error, data }) => {
          if (loading) return "Loading...";
          if (error) return "Error";

          return (
            <ul>
              {data.test02.map(data => <li key={data.id}>{data.name}</li>)}
            </ul>
          );
        }}
      </Query>
    );
  }

  render() {
    return (
      <ApolloProvider client={client}>
        <div>
          <h2>Query test</h2>
          {this.renderQuery()}

          <div>
            <h2>Client query test</h2>
            <button onClick={() => this.setState({ showOtherQuery: true })}>
              Test!
            </button>
            {this.state.showOtherQuery && this.renderOtherQuery()}

            <h2>Store reset</h2>
            <button
              onClick={() => {
                client.resetStore();
              }}
            >
              {" "}
              Reset!
            </button>
          </div>
        </div>
      </ApolloProvider>
    );
  }
}

render(<App />, document.getElementById("root"));

Basically, the button no longer issues the query manually using client.query, but instead sets a boolean that renders an additional Query component, which will then refetch as you expect when the store is reset.

The alternative is manually calling client.query and setting state again (which is basically what the Query component is doing) when the reset button is clicked.

Upvotes: 1

Related Questions