apollo-client: update mutation doesn't work
Intended outcome: updated list of recipes be rendered
Actual outcome: the old list get rendered
How to reproduce the issue: follow what comes here !!!
Versions
hi I have the following client
import React ,{Component}from 'react'
import { Mutation } from "react-apollo";
import {ADD_RECIPE} from '../../mutations';
import Error from '../Error';
import {withRouter} from 'react-router-dom';
import {GET_ALL_RECIPIES} from '../../queries'
class AddRecipe extends Component {
...
onSubmit(event,addRecipe){
event.preventDefault();
addRecipe().then(
({data})=>
{
this.props.history.push("/")
}
)
}
render (){
const{name,category,description,instruction,username} = this.state;
return(<div className="App">
<h2>Add recipe</h2>
<Mutation
mutation={ADD_RECIPE}
variables={{name,category,description,instruction,username}}
update={(cache, {data:{addRecipe}}) => {
const {getAllRecipes} = cache.readQuery({ query: GET_ALL_RECIPIES });
console.log(cache)
//console.log('get all recipes',getAllRecipes)
//console.log('add recipe',addRecipe)
cache.writeQuery({
query:GET_ALL_RECIPIES,
data:{getAllRecipes:getAllRecipes.concat[addRecipe]}
})
console.log(cache)
}} >
{(addRecipe, {data,loading,error})=>
(<form className="form" onSubmit={event=>this.onSubmit(event,addRecipe)}>
<input type="text" name="name" onChange={this.handleChange.bind(this)} placeholder="recipe name" value={name}/>
<select name="category" value="breakfast" id="" onChange={this.handleChange.bind(this)} value={category}>
<option value="breakfast">breakfast</option>
<option value="lunch">lunch</option>
<option value="dinner">dinner</option>
<option value="snack">snack</option>
</select>
<input type="text" name="description" onChange={this.handleChange.bind(this)} placeholder="recipe description" value={description}/>
<textarea name="instruction" id="instruction" cols="30" rows="10" onChange={this.handleChange.bind(this)} value={instruction}> </textarea>
<button type="submit" className="button-primary" disabled = {loading || this.validateForm()}>submit</button>
{error && <Error error={error}/>}
</form>)
}
</Mutation>
</div>)
}
}
export default withRouter(AddRecipe)
and for get Recipe is:
import gql from 'graphql-tag';
//RECIPE QUERY
export const GET_ALL_RECIPIES =gql`
query {
getAllRecipes{
name
_id
category
}
}
`
and get recipe item is :
import gql from "graphql-tag";
export const GET_RECIPE_ITEM =gql`
query($id:ID!){
getRecipeItem(_id:$id){
_id
name
category
description
instruction
createdDate
likes
username
}
}`
About this issue
- Original URL
- State: closed
- Created 6 years ago
- Comments: 29 (5 by maintainers)
Commits related to this issue
- fix: issue with writeQuery See: https://github.com/apollographql/apollo-client/issues/4031#issuecomment-433668473 — committed to vuejs/vue-cli by deleted user 6 years ago
- feat(ui): Redesign, dashboard, local plugins (#2806) * feat: basic fonctionality, welcome and kill port widgets * fix: contrast improvements * feat: plugin/dep/vulnerability widgets design *... — committed to vuejs/vue-cli by Akryum 6 years ago
- Implement ObservableQuery#isDifferentFromLastResult. (#4069) This commit is a more conservative version of https://github.com/apollographql/apollo-client/pull/4032/commits/e66027c5341dc7aaf71ee7ffcb... — committed to apollographql/apollo-client by benjamn 6 years ago
- Support InMemoryCache({ freezeResults: true }) to help enforce immutability. Part of the plan I outlined in this comment: https://github.com/apollographql/apollo-client/issues/4464#issuecomment-46754... — committed to apollographql/apollo-client by benjamn 5 years ago
Why is this issue still opened? It was resolved for me by installing the latests versions of apollo-client and apollo-cache-inmemory
npm install apollo-client@latest apollo-cache-inmemory@latest
Was having the same issue with the UI not reflecting the updated cache. Following the example in the docs, I could not get it working unless I created a new array for
data.todos
:Same issue, using “apollo-cache-inmemory”: “^1.6.2”, “apollo-client”: “^2.6.2”,
@dsanders11 To clarify, modifying result objects returned by
readQuery
only affects the cached result objects, not the data in the store, which can only be modified by writing new results.Modifying data and then writing it back to the store is safe, since it invalidates the previous result objects, so
readQuery
will return new objects next time. That’s what the official docs are doing, unless I’m mistaken.The problem with a deep copy is that it’s expensive, both because the copy itself takes time, and because identical results are no longer
===
identical, which makes equality comparisons in your application (e.g. to determine whether to re-render a component) more expensive. Making a deep copy is something you (as the consumer of the cache) can choose to do, if you want to pay for that convenience / peace of mind. In practice, when you copy your results, you don’t usually need a full deep copy, because you can get away with shallow-copying parts of the result and modifying the partial copy without affecting the original object. Since the cache doesn’t know what parts of the data you’re planning to change, it has no hope of making these decisions correctly. Only you understand the needs of your application. The job of the cache is to enable all use cases, without making assumptions about where you might value safety over performance. If the cache itself always made a deep copy whether or not it was necessary, developers who don’t want/need the copying behavior would be stuck with it anyway. In this area of the system, we’ve chosen to prioritize performance and developer choice over eliminating the possibility of misuse.For those reasons, I think freezing the result objects is the only viable option here, though that would interfere with valid read-modify-write patterns, and would definitely require changing the documentation.
Yup, always ensure the original cache object is immutable! Assign new value by deep clone the object and assign new values will work!
FYR,
"@apollo/client": "^3.0.0-beta.49"
When I useupdate
like this, it does not work.After I set a new object to data like this, it works.
Not sure if this helps, but I found it works if my collection didn’t have id’s returned. When it did have id’s I had to write a fresh object.
eg
@benjamn why are result objects returned by
readQuery
not consistent with the data in the store?readQuery
don’t get data from the store, but return the cached result objects,so if i modify some items, there is no need towriteQuery
to update the store,modifying the result objects is just enough。it seems that there are two cache layers,one is the result objects returned byreadQuery
, the other one is the store. This really makes me confused. I also think the result objects should be immutable and frozen, orreadQuery
should return data from the store every time instead of return the result objects.