How to force Apollo Query component to re-run query when parent component re-renders

Multi tool use
Multi tool use

The name of the picture


How to force Apollo Query component to re-run query when parent component re-renders



I'm using Apollo Client's <Query> within a component that is re-rendered when state is changed within a lifecycle method. I wish to have my <Query> component re-run the query because I know that data has changed.
It appears that Query component is using a cache that needs to be invalidated before query is re-run.


<Query>


<Query>



I'm using a wonky workaround that caches the refetch callback from the render prop in the parent component, but it feels wrong. I'll post my approach in the answers if anyone is interested.


refetch



My code looks something like this. I removed loading and error handling from query as well as some other detail for brevity.


loading


error


class ParentComponent extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.refetchId !== prevProps.refetchId) {
const otherData = this.processData() // do something
this.setState({otherData}) // this forces component to reload
}
}

render() {
const { otherData } = this.state

return (
<Query query={MY_QUERY}>
{({ data }) => {
return <ChildComponent gqlData={data} stateData={otherData} />
}}
</Query>
)
}
}



How do I force <Query> to fetch new data to pass to <ChildComponent>?


<Query>


<ChildComponent>



Even though ParentComponent re-renders when props or state change, Query doesn't re-run. ChildComponent gets an updated stateData prop, but has a stale gqlData prop. As I understand Apollo's query cache need to be invalidated, but I'm not sure.


ParentComponent


Query


ChildComponent


stateData


gqlData



Please note that passing refetch to ChildComponent is not the answer because it only displays information from GraphQL and wouldn't know when to refetch. I don't want to introduce timers or otherwise complicate ChildComponent to solve this - it doesn't need to know about this complexity or data fetching concerns.


refetch


ChildComponent





I added couple options, I always used the first one, the second method would not work if you do not want to pass anything down.
– leogoesger
11 hours ago




2 Answers
2



It seems to me that the Query component doesn't necessarily need to be inside this ParentComponent.


ParentComponent



In that case, I would move the Query component up, since I would still be able to render other stuff while I don't have results in the ChildComponent. And then I would have access to the query.refetch method.


ChildComponent


query.refetch



Note that in the example I added the graphql hoc, but you can still use Query component around <ParentComponent />.


graphql


<ParentComponent />


class ParentComponent extends React.Component {
componentDidUpdate(prevProps) {
if (this.props.refetchId !== prevProps.refetchId) {
const otherData = this.processData() // do something

// won't need this anymore, since refetch will cause the Parent component to rerender)
// this.setState({otherData}) // this forces component to reload

this.props.myQuery.refetch(); // >>> Refetch here!
}
}

render() {
const {
otherData
} = this.state;

return <ChildComponent gqlData={this.props.myQuery} stateData={otherData} />;
}
}

export graphql(MY_QUERY, {
name: 'myQuery'
})(ParentComponent);



Could you refetch in parent component? Once the parent component get an update, then you can evaluate whether to trigger a fetch or not.



I have done it without using Query like the following:


Query


class ParentComp extends React.Component {

lifeCycleHook(e) { //here
// call your query here
this.props.someQuery()
}

render() {
return (
<div>
<Child Comp data={this.props.data.key}> //child would only need to render data
</div>
);
}
}

export default graphql(someQuery)(SongCreate);



So you can trigger your fetch anytime you want it to. You can get the query as a prop in this case.


prop



For your case, you would put your query into a prop using export default graphql(addSongQuery)(SongCreate);. Then call it in your lifecyclehooks DidUpdate.


export default graphql(addSongQuery)(SongCreate);


DidUpdate



Another options is to use refetch on Query.


refetch


<Query
query={GET_DOG_PHOTO}
variables={{ breed }}
skip={!breed}
>
{({ loading, error, data, refetch }) => {
if (loading) return null;
if (error) return `Error!: ${error}`;

return (
<div>
<img
src={data.dog.displayImage}
style={{ height: 100, width: 100 }}
/>
<button onClick={() => refetch()}>Refetch!</button>
</div>
);
}}
</Query>



The second method would require you pass something down to your child, which isn't really all that bad.





in both of your examples child component does something to trigger a rerender. as described in the question, child component doesn't know when to trigger a rerender (it's not interactive). thanks, but this is not really what i'm after
– Andrei R
8 hours ago





no, the first one does not. The parent has the query as a prop, and you can re-render using lifecycle hooks.
– leogoesger
9 mins ago


re-render






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

0qDRP,RIG UtfVb7qAbH7GqWyFHcJSDG,qNek3J,3j1WvvbAC4
02Gt5svl cw0Ye1JoV7RjUcYY VoaHr9CnQ9gV 8TW1uABO0JvNk0 MKqwFPpr0B4zz2uiKPP8LVN,T5iZ,R,4tw2XMD UWYi8I tJ dAK

Popular posts from this blog

Makefile test if variable is not empty

Visual Studio Code: How to configure includePath for better IntelliSense results

Will Oldham