React-apollo update vs refetch - javascript

I am using react-apollo and have been for quite some time. One thing that has already been a problem for me is the fact that refetch doesn't work when using a mutation This has been a know issue for as long as I have been using the app.
I have got round this by using the refetch prop that is available on a query.
<Query query={query} fetchPolicy={fetchPolicy} {...props}>
{({ loading, data, error, refetch }) => {
... pass down to mutation
</Query>
However I am now reading in the documentation that you recieve
an update method as part of a mutation and you should use this to update your application after a mutation.
Can you use the update function to update your UI's data and have it update after finishing a mutation? If you can, is this the standard way to do updates now?
*Using refetchQueries not working
As you can see in the image the console.info() displays that the data.status = "CREATED"; but the request coming back from the mutation directly is data.status = "PICKED"; PICKED is the correct and uptodate information in the DB.

In order of preference, your options are:
Do nothing. For regular updates to an individual node, as long as the mutation returns the mutated result, Apollo will update the cache automatically for you. When this fails to work as expected, it's usually because the query is missing the id (or _id) field. When an id field is not available, a custom dataIdFromObject function should be provided to the InMemoryCache constructor. Automatic cache updates also fail when people set the addTypename option to false.
Use update. The update function will run after your mutation completes, and lets you manipulate the cache directly. This is necessary if the mutation affects a field returning a list of nodes. Unlike simple updates, Apollo has no way to infer whether the list should be updated (and how) following the mutation, so we have to directly update the cache ourselves. This is typically necessary following create and delete mutations, but may also be needed after an update mutation if the updated node should be added or removed to some field that returns a list. The docs go into a good deal of detail explaining how to do this.
<Mutation
mutation={ADD_TODO}
update={(cache, { data: { addTodo } }) => {
const { todos } = cache.readQuery({ query: GET_TODOS });
cache.writeQuery({
query: GET_TODOS,
data: { todos: todos.concat([addTodo]) },
});
}}
>
{(addTodo) =>(...)}
</Mutation>
Use refetchQueries. Instead of updating the cache, you may also provide a refetchQueries function, which should return an array of objects representing the queries to refetch. This is generally less desirable than using update since it requires one or more additional calls to the server. However, it may be necessary if the mutation does not return enough information to correctly update the cache manually. NOTE: The returned array may also be an array of strings representing operation names, though this is not well documented.
<Mutation
mutation={ADD_TODO}
refetchQueries={() => [
{ query: TODOS_QUERY, variables: { foo: 'BAR' } },
]}
>
{(addTodo) =>(...)}
</Mutation>
Use refetch. As you already showed in your question, it's possible to use the refetch function provided by a Query component inside your Mutation component to refetch that specific query. This is fine if your Mutation component is already nested inside the Query component, but generally using refetchQueries will be a cleaner solution, particularly if multiple queries need to be refetched.
Use updateQueries. This is a legacy option that's no longer well-documented, but provided similar functionality to update before update was added. It should not be used as it may be deprecated in the future.
UPDATE:
You may also set up your schema in such a way that queries can be refetched as part of your mutation. See this article for more details.

Related

Apollo optimisticResponse not applied when query is pending

If you have a pending query when a mutation with an optimisticResponse is executed, the optimisticResponse doesn’t get applied.
const {data, refetch} = useQuery(GET_TODOS);
const [updateTodo] = useMutation(UPDATE_TODO);
// in a form submit handler:
refetch();
// Immediately mutate while the _query_ is pending
updateTodo({
variables: { id, description: description + 1},
optimisticResponse: {
updateTodo: {
id,
__typename: "Todo",
description: description + 1
}
}
});
Minimal codesandbox.io example. There’s an artificial 1 second delay link added to make the effect more obvious.
The same behaviour appears to occur with direct cache writes as well; writes will not cause a re-render if there is a pending read query.
The same behaviour can also be witnessed if batching a query in with a mutation.
Is this the intended behaviour? And if so, is there a way to bypass it?
The Apollo useQuery hook uses a default fetch-policy of cache-first. Internally when the Apollo cache is updated the following occurs
Check if any queries are observing that part of the cache
Check if they should be notified of the update
Notify
When checking whether to notify a query, there is a check to see if the query is currently in flight to the server and if so only notify when the fetch-policy is cache-only or cache-and-network.
This is fine, and makes sense, you don't want to spend CPU re-rendering when you know the data is just about to update.
This causes a problem in the example above due to the refetch query being in progress when the optimistic update is applied. The shouldNotify check will return false. Changing the queries fetch policy fixes this
const {data, refetch} = useQuery(GET_TODOS, {
fetchPolicy: 'cache-and-network'
});

Sequential dependent functions in Vuex action

I'm currently building a Vue app that consumes data from the Contentful API. For each entry, I have a thumbnail (image) field from which I'd like to extract the prominent colours as hex values and store them in the state to be used elsewhere in the app.
Using a Vuex action (getAllProjects) to query the API, run Vibrant (node-vibrant) and commit the response to the state.
async getAllProjects({ commit }) {
let {
fields: { order: order }
} = await api.getEntry("entry");
let projects = order;
projects.forEach(p =>
Vibrant.from(`https:${p.fields.thumbnail.fields.file.url}`)
.getPalette()
.then(palette => (p.fields.accent = palette.Vibrant.hex))
);
console.log(projects);
// Commit to state
commit("setAllProjects", projects);
}
When I log the contents of projects right before I call commmit, I can see the hex values I'm after are added under the accent key. However, when I inspect the mutation payload in devtools, the accent key is missing, and so doesn't end up in the state.
How do I structure these tasks so that commit only fires after the API call and Vibrant have run in sequence?
You cannot add a property to an object in Vue and have it be reactive; you must use the Vue.set method.
Please try replacing that forEach block with the following, which adds the new property using Vue.set:
for (i=0; i<projects.length; i++)
Vibrant.from(`https:${projects[i].fields.thumbnail.fields.file.url}`)
.getPalette()
.then(palette => (Vue.set(projects[i].fields, accent, palette.Vibrant.hex)))
);
UPDATE: changing the format from forEach to a conventional for loop may be gratuitous in this case, since the assignment being made is to an object property of projects and not to a primitive.
I'm not spending a lot of time on StackOverflow, and if the above answer works, I am happy for you indeed.
But I expect from that answer you will get console warnings telling you not to mutate state directly.
Now when this happens, it's because while Vue.set(), does in fact help Vue understand reactively a change has been made, potentially deeply nested in an object.
The problem here is that since you are looping the object, changing it all the time, the commit (Mutator call) is not the one changing state - Vue.set() is actually changing it for every iteration.

(GraphQL) - Why does Apollo check the cache first, before doing anything else?

We are developing APIs using Apollo GraphQL. We use the out of the box caching solution that Apollo provides (KeyValueCache using a Redis datastore).
When a request query arrives, why does ApolloServer check the cache first before it does anything else?
Is there any way to insert logic before the cache is touched? For example, we want to do some authentication and permissions checking before Apollo checks the cache.
(Yes, there are directives but we find Public/Private scope and maxAge insufficient for our needs.)
The code and explanation below flags a few different approaches for you to explore -- hopefully one will suit your needs (I am assuming you know you can control whether Apollo looks to cache first by fetchPolicy - although I discuss this briefly below). First, consider using a HOC that checks permissions and authentication prior to returning the passed Component. The permissions/auth data can be passed as props should the passed Component be rendered.
withUserData = Component => {
const { isValidated, userData } = checkAuthAndPermissions(); // Modify for your implementation
if (!isValidated) return null;
return <Component userData={userData} />
}
You can thereafter wrap any Component that needs to make the auth/permission check with the HOC, as shown below. As also shown below, Apollo provides the opportunity to skip the query altogether looking to props or other logic, if that is something you may consider. Finally, through the option prop, you have the ability to set the fetchPolicy, which could be dynamically based on a permission check or props. With this fetchPolicy you could avoid looking to cache if that is an objective.
const ComponentWithApollo = graphql(YOUR_QUERY, {
skip: props => { /* consider permissions/auth here, skip if needed */ },
options: props => {
const fetchPolicy = determineFetchPolicyFromAuthOrPermissions();
return { fetchPolicy };
},
props: ({ data }) => data
})(YourComponent);
withUserData(ComponentWithApollo);

Sharing a State Array

Have kinda a unique question, in my code I have a listener to a database that loads down objects into an array.
All I do when I load it in is
AddObject(obj){
this.setState({
Data: [...this.state.Data, obj]
});
}
Pretty simple. However this listener function, there is no exact time when need data will be added. When I go to use that Data sent in Data, I went to pull it out of the Data Array, however I am worried if I try copying data out of the array, or removing the "seen" data, I will get weird behaivor if my listener function triggers and I try adding data to the array at the same time.
Is there some sort of a way to do this? I guess you could call this a shared resource
Ideally, I would have something like this:
loadDataIN(){
var LengthToGrab = this.state.Data.length;
//we need to remove this length, now any new data will be added to index 0
}
Does this make sense? basically I am trying to figure out the best way to remove data from this array, and not have to worry about overwritting, or losing data. Maybe some sort of processing que
From official doc
setState() enqueues changes to the component state and tells React
that this component and its children need to be re-rendered with the
updated state.
You don't need to worry that two kinds of situation would have conflict in the same time.
setState() enqueues the pending state before the changes be rendered.
In fact, no matter how mechanism be implemented, React is a framework of JavaScript which is working on a model event-loop.
So if you want to pull out the data from this.state.Data:
loadDataIN(){
this.setState(function(prevState, props) {
// this.fetchData = prevState.Data;
return {
Data: []
};
});
}

ParseReact realtime like Firebase and MeteorJS

I started using ParseReact (https://github.com/ParsePlatform/ParseReact), but i want to know if there are any way of realtime data ? Like in MeteorJS or Firebase.
To add Parse data to a component, it simply needs to subscribe to a standard Parse Query. This is done through an implementation of the newly-proposed observe() API for React. The ParseReact Mixin allows a version of this new lifecycle method to be used today with Parse Queries.
If you're using React with ES6 classes, we also provide a subclass of React.Component that allows you to use the observe() and Query-specific APIs.
var CommentBlock = React.createClass({
mixins: [ParseReact.Mixin], // Enable query subscriptions
observe: function() {
// Subscribe to all Comment objects, ordered by creation date
// The results will be available at this.data.comments
return {
comments: (new Parse.Query('Comment')).ascending('createdAt')
};
},
render: function() {
// Render the text of each comment as a list item
return (
<ul>
{this.data.comments.map(function(c) {
return <li>{c.text}</li>;
})}
</ul>
);
}
});
Whenever this component mounts, it will issue the query and the results will be attached to this.data.comments. Each time the query is re-issued, or objects are modified locally that match the query, it will update itself to reflect these changes.
Mutations are dispatched in the manner of Flux Actions, allowing updates to be synchronized between many different components without requiring views to talk to each other. All of the standard Parse data mutations are supported, and you can read more about them in the Data Mutation guide.
// Create a new Comment object with some initial data
ParseReact.Mutation.Create('Comment', {
text: 'Parse <3 React'
}).dispatch();
I tried the example, but always have to reload view. It`s not the same as Firebase and MeteorJS
I would also like to hear more about this...Not sure if this is actually a supported feature or not. As the documentation states, Queries you are subscribed to in the observe function will be updated with new props/state, as well as any time a Mutation occurs. In this sense it is very much like Meteor in that changes changes to state (much like changes to Session variables) can reload queries to the backend.
Where it differs is that, unlike Meteor, changes in Parse (say, directly in the db or from another front-end instance) are not communicated to all subscribed React front-ends. At least as far as I can tell. Which is kinda disappointing. Would love to hear from someone more experienced, who hasn't just been messing with ParseReact for the past 24 hours.

Categories