Open dustinfarris opened 7 years ago
The error happens when I leave the index page and on the new page cashay queries new data from the server.
Ok, starting to understand:
What I'm still struggling with is how, if the maps-index response is flushed, does the maps-index component continue to render. For example if I:
Getting closer (without mutation handlers this time):
When the user visits maps index, cashay stores the result and entities in redux, e.g.:
{
cashay: {
entities: {
Map: {
1: { id: "1", name: "First Map" }
2: { id: "2", name: "Second Map" }
3: { id: "3", name: "Third Map" }
}
}
ops: {
maps-index: { variables: {}, status: "complete" .. }
}
result: {
maps: [
"Map::1"
"Map::2"
"Map::3"
]
}
}
}
cashay also caches a "response" object in memory, which, as far as i can tell at this point, is the denormalized result of the query, be it from the server, or from redux (or both i suppose in some cases).
So long as this "response" object exists, cashay will not ask redux, much less the server for an update. This is why mutationHandlers are necessary when there is a "response" object" in memory for a particular query.
When my user clicks a specific Map, they are taken to a detail page that executes a new "map-detail" query. As part of this operation, cashay flushes the above-mentioned response object for the maps-index operation. Then let's say the user executes an update mutation to change the name of the map. Cashay updates redux so it looks something like:
entities: {
Map: {
1: { id: "1", name: "CHANGED NAME!" }
...
When the user navigates back to the maps index page, cashay flushes the response for the map-detail query. Cashay also sees that it no longer has a response object for the maps-index query. So it presumably checks redux for an existing result. And it finds one! So it pieces together a new response, but entirely from local state (redux). Because cashay updated entities
during the mutation, the new response for the maps-index query contains the updated name. All is good.
Then the user clicks the same map and is taken back to the detail page. Cashay sees no "response" object for map-detail (it was flushed), but is able to piece together a new response from redux. Because no server query is required, the maps-index response object is not flushed!
The user changes the name again. Cashay executes a mutation.
The user navigates back to the index and doesn't see the change!
This is because cashay found that the response object for maps-index was still there and used that instead of checking redux.
How do we force flush a response when the user leaves the maps-index page but has not triggered a new server query?
Then the user access yet another map which again flushes the response for the maps-index query.
Then, the user clicks a big 'ol PLUS icon in the navbar to add a new map.
Then the user fills out a new map name and mutation is executed. There are now 4 maps on the server.
The user navigates back to the maps index page, but sees just 3 maps!
This is because cashay, again, saw that there was no longer a denormalized response for the maps query, but there is still a result entry for the query. But that value (still) looks like this:
result: {
maps: [
"Map::1"
"Map::2"
"Map::3"
]
Still 3 maps.
How do we update the result
for "maps"? Can it be done without re-triggering a server request when we already have all the data we need?
@mattkrick i think this issue is ready for your eyes. This last comment I think summarizes the problem pretty well, but see above for more context.
Side note: :point_up: I suspect that this problem goes away when @live
is introduced. But I'm not quite there yet with my server (Elixir/Phoenix) so I'm stuck with the stale result data shown above for now.
there's a lot to dig into & i'm not sure i grok it all, but if stale data is getting to the view layer, then it has more to do with the ember integration i think. you shouldn't ever have to manually flush a result because the result will change whenever a mutation occurs as long as there is a mutation handler.
tl;dr,
This is how everything works great with a mutation handler:
Continuing, this is how it can break down:
Sent from my iPhone
On Jan 11, 2017, at 5:01 AM, Matt Krick notifications@github.com wrote:
there's a lot to dig into & i'm not sure i grok it all, but if stale data is getting to the view layer, then it has more to do with the ember integration i think. you shouldn't ever have to manually flush a result because the result will change whenever a mutation occurs as long as there is a mutation handler.
— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub, or mute the thread.
@mattkrick there may very well be some nuance at play with the Ember view layer, but I think there is a real bug here, and I think the reason you may not have run across it as much or at all in the action project is because your use of @live
circumvents your need for mutationhandlers in this kind of scenario.
I'm not sure where this is breaking down either in Cashay or in my own code; but here's the reproducible scenario:
Note: I typed this up by hand (no copy+paste) so if there's some stupid syntax error its probably just a typo in this GH issue alone.
The SETUP
Two routes:
/maps
/maps/:map_id
Starting with the second route: a detail page that can create and update maps. Creating is done by clicking "NEW" on the index page, which triggers an action that generates a UUID, sets a
isCreatingNewMap
flag in redux state, and redirects to the detail page.The detail page has two queries and two mutations.
The two mutations are
createMap
andupdateMap
The queries are identical except one has
@cached
and is used when the map is being created. This is so thecreateMap
mutation knows to grab subfields.This is the relevant piece of the
MapDetail
component:Back to the index page, this is the relevant piece of the
MapsIndex
component:The SCENARIO
Part 1 (The happy version)
Step 1 - User visits index
Cashay fires a server query:
All maps are displayed with their name
Step 2 - User clicks "NEW"
And is taken to a detail page with a freshly minted UUID in the URL
No server request. Cashay executes
cachedMapQuery
shown in the component code above.Step 3 - User fills in name, triggers Save
Cashay executes a mutation:
map-detail
optimistic mutation handler fires!maps-index
optimistic mutation handler fires!then sends a server request
Step 4 - User navigates back to index
As part of this transition away, the detail route dispatches to turn off
isCreatingNewMap
. This triggers a redux subscribe callback. TheMapDetail
component, which has not been torn down yet, runsstateToComputed
one more time which triggers a cashay.query, but this time usingmapQuery
instead ofcachedMapQuery
(shown in the component code above). This triggers a (unnecessary?) server request:The transition completes and the user is now on the index page. The new map appears under the rest with its name. Everyone is happy so far.
Step 5 - User clicks the new map going back to its detail page
As in step 4, the map is looked up using
mapQuery
(the one without@cached
). No new server queries—presumably because of the side-effect query in step 4, Cashay must be using the cache from that query.Step 6 - User edits the name of the map and triggers save
map-detail
optimistic mutation handler fires!maps-index
optimistic mutation handler fires!And Cashay kicks off a mutation request to the server:
Step 7 - User navigates back to the index page
No new server queries. The maps new name appears as it should.
Part 2 (The sad version)
The difference here is we'll be editing an existing map's name, instead of creating a new map and then editing it.
Step 1 - User visits index page
Cashay fires a server query:
All maps are displayed with their name
Step 2 - User clicks an existing map and taken to detail page
Cashay executes
mapQuery
in the component as shown in the component code above:Step 3 - User edits the map's name and triggers Save
map-detail
optimistic mutation handler fires!maps-index
optimistic mutation handler throws error!