Reputation: 177
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
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
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