I'm having trouble understanding a weird behaviour when using Apollo Client.
Here is a little bit of context, maybe I missed something:
I have an app that displays lists of items.
Thanks to routing, each list has its own route :
/list/:listId
Each list has its own settings, and these settings are used to know which item details are displayed (per se, on one list I can decode to show the price of my item, and hide it on another list)
Now, the problematic part: I'm using the apollo client useQuery hook to fetch my list. the listId is obtained through the react-router-dom useParams hook, and then passed as a variable to useQuery, and that's my whole point:
useQuery(ITEM_LIST, {
variables: {
listId,
}
})
Even when passing a new listId by navigating from a list to another, apollo still fetches the settings from my previous list, for a very short moment, resulting in a glitchy user experience (details that should not be displayed are displayed for a moment, then immediately disappear)
I read a few articles regarding apollo caching issues, such as this one, but still, didn't manage to find the source of this problem. Here are the things I've tried so far:
alias the id field in my query
make sure my server returned a response with a unique ID
Has anyone seen this before?
Related
So basically, I have a simple React app connected to Firebase that lists different types of food from firestore collections.
Example:
I have a few categories. The default one is "All" that displays top 8 popular dishes from all other available categories and this part is easy but I want an user to be able to click on other category and update my query.
Category is actually a NavLink that updates location on click so: if user click on "Pizza" category the url looks like this localhost:3000/Pizza if he clicks on Salad it is localhost:3000/Salad etc.
I have a "Wall" component that is a section and it displays those items from firestore.
My query ref in this wall component look like this: const foodRef = db.collection("food").doc("all").collection("items");
But I want to set .doc dynamically and make query on every update so I changed the query to something like that:
const location = useLocation();
const foodRef = db.collection("food").doc('${location.pathname}').collection("items");
And when user click on different Card (NavLink) url updates but query does not.
I know it is a bad solution but I actually have no idea how to do that.
I have read about Recursive Paths in react router but I do not know if it is what I am looking for.
If you know how to approach that please let me know.
Thanks for your time.
Firestore does not support wildcards or replacements in queries and Query objects are fully immutable (they can't be changed). You have to know the names of the documents and collections ahead of time to build a query. If you want to change some part of a query, you have to rebuild a whole new query object every time, and run the query again to get a new set results.
If I want to display many posts in my web application but every post have its own type and I want to display each type in single page so, What's the best method to do that? Is put all all posts in one url and use query string to filter the posts upon the type and display it in the page?
For example : axios.get('/posts?type =sport')
Or I have to put every single type in separate Url
For example: axios.get('/posts/sport')
Also one more question please?
use one reducer to manage every posts or create one reducer for each post type?
you can add a dynamic route to every new type.
Ex:
'/transaction' -> component-1
'/transaction/:type' -> component-any (multiple)
welcome to Stackoverflow!
I can imagine you have a web API of some sort serving a URL /posts. You want to consume that endpoint from your web application, and you are using axios to do that. I can assume you are using JSON to return that data. Correct me if I'm wrong.
Now that the basic information is "clear", what data you serve from the endpoint, and how it is requested from the client is up to you. Do you want to ask the server what types are there first, and then do one AJAX request per type? Ok. Do you want to serve all posts independent of their type? Ok. Do you want to accept POST data in your controller so you can filter the results before returning a response? Ok.
If you are looking for a more specific answer, you must give more details, or specify more. But I hope I could be of help.
Edit: complete answer.
If you want to filter the results, you have to send some additional data in your POST request, in this case, your post type. In axios, this could be done like this:
axios.post('https://example.com/posts', {
type: 'sports'
}).then((data) => {
console.log(data);
});
You can obviously get the "type" value from a select input, other variable, even the current router page. I don't know your exact setup, but you can always come back and ask ;)
THEN, in your API controller you have to get that POST parameter type, and use it to filter the results. Again, I don't know your exact setup, but for MySQL if would be a WHERE statement in your query, or similar.
This is more of a open question but hopefully it won’t get deleted.
I am using react and apollo although the question is more general.
Let’s say I have 3 distinct views in my app all using similar (but not the same) data.
All of them are using separate queries but each of the query uses common operation but with slightly different data returned.
Let’s say I have a mutation somewhere that adds something to data (think of a list of items and a new item being added).
Let’s say after mutation I want to update cache to reflect that change. I am using read/writeQuery to do the update.
With this setup I need to update 3 queries - this becomes a maintenance nightmare.
After some reading I figured I am doing this wrong - I have now created a single query - now I need to only update that single query after mutation and all of my views are updated automatically.
However the problem is that this query now has to download all the data that all 3 views combined need - feels like this is very inefficient, because some of the views will get data they'll never use.
Is there a better way to do it?
Please note that read/writeFragment won't work because they won't update the underlying queries - check this answer for example: https://stackoverflow.com/a/50349323/2874705
Please let me know in comment if you need a more concrete example.
All in all I think in this setup I would just be better with a global state handling and avoid apollo cache all together - however I feel cheated cause apollo promised to solve the state problems :)
EDIT
Here's a concerete example:
Let's say our graphql schema is defined liked this:
type Post {
id: ID!
title: String!
body: String
published: Boolean!
}
type Query {
posts(published: Boolean): [Post!]!
}
type Mutation {
createDraft(body: String!, title: String!): Post
publish(id: Int!): Post
}
Now, we create 3 queries and 2 mutations on the client
query PostTitles {
posts {
id
title
}
}
query Posts {
posts {
id
title
body
published
}
}
query PublishedPosts {
posts (published: true) {
id
title
body
published
}
}
mutation CreateDraftPost ($body: String!, $title: String!) {
createDraft(body: $body, title: $title) {
id
title
body
published
}
}
mutation PublishPost ($id:ID!) {
publish (id: $id) {
id
published
}
}
Just to note createDraft creates a post with the default false published value.
How can use either of those mutations to create or publish a post and have all the 3 cached queries to be updated without using refetchQueries or manualy updating each of the query?
I think the real problem is that each of those queries are stored separately in the apollo in-memory cache.
From my experience, here's how it should goes.
In the case of CreateDraftPost mutation:
You call the mutation and also pass an update function. In this update function, you modify the cache of the root query posts by creating a new fragment of Post and then add this fragement into posts. See this: https://www.apollographql.com/docs/react/data/mutations/#making-all-other-cache-updates
Since the PostTitles and Posts all rely on the root query posts (just differ in the queried fields) and the new fragment of Post you've just added into posts has sufficient fields, your PostTitles and Posts should automatically reflect the changes.
Since CreateDraftPost always create a draft with published defaults to false. You don't need to update anything related to PublishedPosts query.
In the case of PublishPost mutation:
You call the mutation and the returned result is a Post with updated fields (id, published). By the mechanism of Apollo GraphQL cache, this Post (identified by id) will be updated in any queries it has involved. See this: https://www.apollographql.com/docs/react/data/mutations/#updating-a-single-existing-entity
However, you need to manually update the PublishedPost query. Do this by providing update function in the mutation call. In this update function, you will readQuery of PublishedPost first, create a new Post out of the returned data and finally writeQuery to add this post into the PublishedPost results. Reference this: https://www.apollographql.com/docs/react/caching/cache-interaction/#combining-reads-and-writes
How about using refetchQueries:
In the case of CreateDraftPost mutation, refetch only Posts query should be sufficient (the PostTitles should be updated accordingly) since both Posts and PostTitles rely on the same root query posts and fields in Posts has also covered fields in PostTitles
In the case of PublishPost mutation, I would prefer refetch the PublishedPost query to avoid doing the whole update thing (since I'm lazy and I think it will not cost me much to refetch 1 query)
It sounds like you've looked into and used the update argument that can be passed to mutation functions returned from useMutation. You're probably using proxy.readQuery and proxy.writeQuery to update it (or letting this magic happen in the background). If not, here is the documentation.
Another approach that is similar in concept but finer detail is to use proxy.readFragment and proxy.writeFragment. You can specify a set of properties on a type as being part of a fragment, and update that fragment whenever new data comes in. The nice part is that this fragment can be used within any number of queries, and if you update the fragment, those queries will update.
fragment documentations
I want to build a search page that lets user query data based on certain criteria that they can choose from select and radio checkboxes, on behalf of which a query string is generated that gives the key/value pair for searching, the functionality is somewhat similar to what they have for their staters page.
Please check the link below to get an idea of what I am looking for:
https://www.gatsbyjs.org/starters/?v=2 <---- query string.
These pages need to be shareable so query strings are essential and data fetching need be as quick as it is on their website.
From what I have understood so far, according to documentation:
https://www.gatsbyjs.org/docs/data-fetching
I have two options:
Get data during build time
Get data during run time
For a run time, I think, I would need to use redux-thunk to make network calls to an ExpressJs, MongoDB REST API but my concern is that it can be slow.
For build time, I am sure on how can I source data from Graphql queries, and render them.
Any help on this will be great. Thanks
The Gatsby Starters Library is parsing the URL param in this urlToSearch method in the PluginSearchBar component:
urlToSearch = () => {
if (this.props.location.search) { // get the params from the location prop
const match = /(\?|&)=([^&]+)/.exec(this.props.location.search)
if (match) return decodeURIComponent(match[2])
return ``
}
return ``
}
Mainly, it uses the Gatsby location prop (from #reach/router under the hood) to get the search keyword from the query string.
Search is then implemented client-side using Algolia's react-instantsearch library. You can find the complete implementation in plugin-searchbar-body.js.
There are naturally other ways to implement search. A good place to start for inspiration is Gatsby's Adding Search documentation.
I am looking for help regarding best practice using Apollo, React and Meteor.
I linked the Meteor.user() model to a Schema in Apollo and I can now access it thanks to a Query Component. I have a query that looks like this :
gql`
query User {
user {
_id,
email
}
}
`
and it does the job, with a resolver giving directly the email adress. However I need it in different places and for every component where I need it I am making another < Query > component with the same Query I copy paste from on file to another. It seems to me that I am loosing the all point if many of my components are querying again and again the same things. However I do not manage to find the solution to this "DRY" problem. There are not yet so many examples including the Query component from react apollo so if someone could help me with this it'd be much appreciated.
By default, the Apollo client uses a fetchPolicy of cache-first. That means if the result of the query is already in the cache, it will be fetched from there and no network request will be made. That allows you to use the same query across multiple Query components without having to worry about making the same request to your server over and over again.
You can specify the fetchPolicy for a particular Query component if you want to override this default behavior -- for example, maybe you want to always fetch new data from the server, in which case you would use network-only or maybe cache-and-network. See the docs for more details.
NOTE: A common "gotcha" is that the cache uses the id (or _id) field to normalize the cached results. That means your queries have to include the id field (or provide a custom implementation of dataIdFromObject) to see the expected behavior. See this page for additional details.
In terms of keeping things dry, it's common practice to store your queries in one or more separate modules and then import them as needed. So you could have a queries.js file like this:
import gql from 'graphql-tag'
export const USER_QUERY = gql`
query User {
user {
_id,
email
}
}
`
graphql-tag comes with a loader that lets you import queries directly from .graphql/.gql files if you're using Webpack. Check out the recipe here. There's also a babel plugin for doing effectively the same thing (checkout it out here). Any of these approaches should reduce the redundancy in your code.
EDIT: As pointed out in #camba1's answer, fragments can also be used to DRY up your queries:
query User {
user {
...userFields
}
}
fragment userFields on User {
_id,
email,
}
Another thing that may be useful to avoid having to copy paste query code all over the place is to use query fragments .
for example:
# Query that contains a fragment
query myQuery1($_key: ID!) {
myQuery1(_key: $_key) {
field1,
...myFragmentFields
}
}
# Fragment to be used in queries
fragment myFragmentFields on myQueryType {
_key,
name,
formula,
type
}
Here is the documentation:
You can use cache-only or cache-first policy in query.
Docs