What is the best practice for triggering query refetch from another component / page?
I have two pages - list and form - and I want the list to update when I submit the form.
How can I do that?
Sometimes I really suck at being a React developer :(
Why did you close this? Which solution did you go with? I'm very interested to find out if there is a common best practice for this since I think it's a very normal use case. Thanks.
Please provide a solution. It'd be very helpful.
What's the design pattern to actually achieve this?
Anyone can point in the right direction please? 👍
Have you guys read the docs here? http://dev.apollodata.com/react/cache-updates.html
Probably the easiest is to use the refetchQueries option.
Also covered in the tutorial here: https://dev-blog.apollodata.com/react-graphql-tutorial-mutations-764d7ec23c15
I think this problem is not really an issue when doing a mutation. I only encounter this problem when I want to update other queries based on the result of a query.
I think the issue is that the component firing the refetch is different than the component rendering the data
Hi,
I have this problem too, @webberwang , you're right!! The issue is refetch from another component that render the data.
Example:
Component A do some query fetch.
Component B is a button that trigger mutation and refetch query fetch.
I've tried format like this
mutate({
mutation: xxxxx,
variables: {
...values
},
refetchQueries: yyyy
})
The refetchQueries are triggered successfully, but, the component A are not rerendering, networkstatus , loading are still same.
Tried notifyOnNetworkStatusChange: true options , but not helping
I've encountered the same issue. I can confirm that the issue is that the component firing the mutation (and refetchQuery) is different from the component rendering the data - just as @charlielukman described.
The apollo cache is updated just fine by the refetchQuery.
The two components share the same parent component, but even forcing an update of the parent component (calling this.forceUpdate() or changing its state) does not cause component rendering the data to update.
Did anyone find a solution for this scenario?
Having the same problem as @dybzon
It would be great if this could be re-opened as it is, I think, a very valid question. I'm facing exactly the same problem. I've a list of items displayed in one component, and a completely separate modal component that mutates one. Of course, the refetchQueries in my modal component doesn't help at all.
Hi,
I try another approach, which is using apollo state.
For my case :
Component A doing query to fetch data, which is consume parameter from apollo state.
Component B that trigger mutation, set the state.
When state change, the query in component A automatically re-trigger
It works fine for me. Hope it helps. :smiley:
Interesting idea @charlielukman, I'll give that a try, thanks!
Hey Team,
This probably won't work for every use case however mine required that I update one component from information being submitted in a subcomponent.
Since I knew what the changes were supposed to look like and where they were supposed to go I opted to use "update" inside of the mutation to give me a space where I could utilize readQuery and writeQuery on the cache. No refetch necessary
Here's a quick example of that. In this case, removing a friend from a friend's list. the removeFriend const was then piped into a compose(...) with some other stuff.
const removeFriend = graphql(RemoveFriendMutation, {
props: (props) => ({
removeFriend: friend => {
return props.mutate({
variables: {
friend: friend
},
update: (store, {resp}) => {
const data = store.readQuery({
query: FriendsListQuery,
});
let pruned = // Some code to remove a user from data.getFriendsList.friends
data.getFriendsList.friends = pruned;
store.writeQuery({
query: FriendsListQuery,
variables{
// LIST NECESSARY VARIABLES HERE. these select the related query from your cache
},
data
});
}
})
}
})
})
Maybe worth noting that in my case the "necessary variables" I mentioned were just "count: null, nextToken: null". On a page where I happen to be pulling everything.
The cache stores data by using a combination of the query and those variables, so if I had another with "count: 5" cached then I'm thinking I might run into some trouble
So if my understanding of the question is correct I think I solved it (wanting to trigger refetchQueries independently of a mutation.) Try client.queryManager.refetchQueryByName(queryname) as a one liner you can use anyplace after client is called.
For example, I want my mutation update function to attempt to write the mutation results to cache but if it can't find the result or push the changes only then do a refetch on the associated query:
mutate({
variables: mutation_vars,
update: (cache, { data }) => {
const mutation_data = data[mutation_type][mutation_name];
const current_cache = cache.readQuery({
query: query,
variables: query_vars,
});
try {
// Attempt to merge mutation result data into Apollo cache and rewrite to it
current_cache.Location[0].children.push(mutation_data);
cache.writeQuery({
query: query,
variables: query_vars,
data: current_cache,
});
}
catch(error) {
console.error("Mutation data not merged with Apollo cache, check pathing:\n",error);
// In the event we can't update the cache, we can refetch associated queries so the
// result are still added without refreshing the page.
client.queryManager.refetchQueryByName(query_name);
}
}
});
As for problems others are having updating multiple components after a mutation, using a subscription-based solution with the one liner client.queryManager.refetchQueryByName(query_name) seems like it would work.
@arvinsim redux maybe can help you
refetchQueries should work out of the box, no additional code is needed. client.query works for me (all Query components are updated correctly).Hope this helps!
Here is a nice docs about it https://www.apollographql.com/docs/react/essentials/queries.html#refetching
Let's elaborate our problem and solving solution.
The refetchQueries totally works, but it only refetch with the last variables.
According to the source, the cache key is operationName + variables, but refetchQueries only takes care the operationName.
Our solution is: add the namespace through webpack, and refetchQuries supports regexp.
For example:
// in ComponentA
import foo from 'foo.gql?prefix=A';
// in ComponentB
import foo from 'foo.gql?prefix=B';
// in ModalFrom
import bar from 'bar.gql';
import { graphql } from '.../ourGraphql';
@graphql(bar, {
name: 'bar',
options: {
refetchQueries: /^.*foo$/,
},
})
Hope this is helpful.
Here's a naive approach if you have a parent/child setup. Since apollo will refetch a query when state is updated I just pass props to the child and then setState(). It would seem like all you would have to do is just change state on the parent component and the child would update, but I couldn't get that to work. Here's what does work for me. Maybe it will work for your use case:
````
// parent component
...
import Child from './path to child'
...
class Parent extends React.PureComponent {
state = {
something: "hi"
}
handleChange =( ) => {
this.setState({
something: "bye"
})
}
render() {
return (
/>
)
}
export default Parent
}
// child component
...
import {Query} from "react-apollo"
import gql from "graphql-tag"
...
const getSomething = gql
query getSomething (
$something: String
) {
getSomething (
something: $something
) {
something
}
}
class Child extends React.PureComponent {
state = {
something: ""
}
// THIS IS THE IMPORTANT PART
componentWillReceiveProps() {
this.setState({
something
})
}
render() {
return (
variables={{
something
}}>
{({loading, error, data}) => {
if (loading) {
return
}
if (error) return
return
}}
)
}
export default Child
````
My solution is little different from every one else, I basically expose the refetch function from the query component.
For example
class List extends React.Component{
constructor(props){
this.exposedFunctions ={};
}
render(){
return <Query ...>
{(data,loading,error,refetch)=>{
this.exposedFunctions = {refetch}
return render stuff here
}}
</Query>
}
}
Then the parent can use ref to call this refetch function. like this
class Parent extends React.Component{
listRef=null;
reRenderList = ()=>{
if(listRef){
listRef.exposedFunctions.refetch();
}
}
render=()=>{
return <List ref={input =>this.listRef=input;}/>
}
}
I know this is probably little anti-pattern, but this, in my opinion is better than handling passing a "fake" changed prop to the child component and force it to rerender and handle the prop.
Has client.queryManager.refetchQueryByName(query_name) disappeared from the API? I no longer see it.
I guess some of you came here looking for a solution to trigger a refetch on a <Query />component by executing a command outside of the component itself. Here's my solution for you, might seem a little bit patchy but it's not anti-pattern to React.
Your query component should look something like this:
<Query query={YOUR_QUERY}>
{({loading, error, data, refetch}) => {
if(loading) {
// Do your thing
return
}
if(error) {
// Do your thing
return
}
return (
<div>
<button ref={(buttonRef) => this.refetchQueryButtonRef = buttonRef} hidden={true} onClick={() => refetch()}/>
{/* Do stuff with data */}
</div>
)
}}
</Query>
Now you can create a method with access to refetchQueryButtonRef to trigger a click and therefor a refetch:
_handleTriggerToRefetchQuery = () => {
this.refetchQueryButtonRef.click()
}
Hope it helps.
Since refetchQueryByName was removed here: https://github.com/apollographql/apollo-client/commit/f02baa0a2608de7a441c716698fdabd33c6cd90c#diff-999fe4a4ffd1fcac651edebf22954809L1412
had to come up with a workaround:
const findQueries = (manager, name) => {
const matching = []
manager.queries.forEach((q) => {
if (q.observableQuery && (q.observableQuery.queryName === name)) {
matching.push(q)
}
})
return matching
}
const refetchQueryByName = (name) => {
return Promise.all(findQueries(client.queryManager, name).map(q => q.observableQuery.refetch()))
}
Be warned that queries is also a private API, just like refetchQueryByName used to be.
We've disabled cache due to concerns over real-time resposiveness of the app since we can't use subscriptions due to backend limitations. I suppose there's not much to do about this problem without cache right? I mean technically I could just take the refetch render prop from the component I want to rerender and pass it to the parent which would then pass it to the child with mutation that is supposed to trigger the rerender but that seems like ugly and hacky workaround.
I just added fetchPolicy: 'no-cache'. And when i attempted to update the parent component the child did it.
const QueryExecutor = graphql(data, {
options: props => ({
fetchPolicy: 'no-cache',
notifyOnNetworkStatusChange: true,
variables: {
artistName: props.plaza
},
fetchPolicy: 'no-cache'
})
})
@juanptcZXV This is exactly what I was looking for! Thanks.
https://medium.com/@martinseanhunt/how-to-invalidate-cached-data-in-apollo-and-handle-updating-paginated-queries-379e4b9e4698
I have two-component which one of them is getMovies , other one addMovie (form)... How can I re-fetch movies in addMovie component?
addMovie({
variables: { title: title, description: description, year: parseInt(year), directorId: directorId },
refetchQueries: [{ query: getMoviesQuery }]
})
https://www.apollographql.com/docs/react/data/mutations/ ctrl+f = refetchQueries
I use useQuery ,useMutation hooks
Triggering refetch from another page so that the data to be rendered is updated in another page component is a quite common case with React Native as each screen has its own updated state with react-navigation. Is there an actual example of how one could do it? I don't understand why would such a great library would require its users to come up with "workarounds" for such an important use case.
I came here to find a solution for this issue. In my case, I want to save some query requests in a Profile screen (after changing some settings in Setting Page) and not fetch all the time the data from the API.
I think I might have found a simple workaround, so bear with me for the example:
Let's identify the page:: 1 <-> Profile screen; 2 <-> Setting screen
screen 1 to screen 2 one uses react-navigation with a component name such as onPress={() => navigation.navigate('Settings')} screen 2, the user clicks "Save" button, or if it doesn't exist on the UI, the back arrow, or an automatic redirect. In each case, it's possible to attach a payload to the navigation property, such as onPress={() => navigation.navigate('Profile', { change: true })} and trigger and update the Profile screen accordingly, with a condition on the refetchQueries option (condition: true or false depending on the payload).Hope this helps.
Though it's possible to solve this problem manually, I agree with @DalerAsrorov. One of the key benefits of this library is its declarative philosophy.
Triggering refetch from another page so that the data to be rendered is updated in another page component is a quite common case with React Native as each screen has its own updated state with
react-navigation. Is there an actual example of how one could do it? I don't understand why would such a great library would require its users to come up with "workarounds" for such an important use case.
I might be off topic here, but I managed to do this (refetch data from Parent when a child have performed a Mutation) by simply using the "refetch" provided by "useQuery" hook ...
So the parent Component would have something like
const { error, data, refetch } = useQuery(MY_QUERY)
Then I pass it down to the Child
<ChildCompoent {...{refetch}} />
Then finally, when the child have performed a mutation, I just call "refetch" from the Child that performed the mutation. This will trigger the refetch on the Parent and re-render the new data ...
refetch()
@Haja-andri Your solution worked for me. Thank you!
Most helpful comment
Sometimes I really suck at being a React developer :(