mattkrick / cashay

:moneybag: Relay for the rest of us :moneybag:
MIT License
453 stars 28 forks source link

Conditionally cached? #153

Closed dustinfarris closed 7 years ago

dustinfarris commented 7 years ago

Is it possible to do a @cached query but actually do the query if it turns out the model doesn't exist on the client?

My scenario (continuing from #141):

In this scenario, the data for the detail route is already on the client, because the client filled out a form. But going forward, if the user visits this page, it will need to retrieve data from the server.

Is there a way to sometimes check cache, sometimes not?

mattkrick commented 7 years ago

That's just a regular query, it won't ask the server for more than it needs.

On Dec 27, 2016 5:53 AM, "Dustin Farris" notifications@github.com wrote:

Is it possible to do a @cached query but actually do the query if it turns out the model doesn't exist on the client?

My scenario (continuing from #141 https://github.com/mattkrick/cashay/issues/141):

  • User visits /items/new and fills out a form, clicks "Create!"
  • cashay.mutate('createItem' ... is executed with a pre-made unique id
  • even though the server has not responded yet, user is immediately redirected to /items/123 where "123" is the client-generated unique id
  • the detail component executes cashay.query(getItemQuery ... which hits the server

In this scenario, the data for the detail route is already on the client, because the client filled out a form. But going forward, if the user visits this page, it will need to retrieve data from the server.

Is there a way to sometimes check cache, sometimes not?

— You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub https://github.com/mattkrick/cashay/issues/153, or mute the thread https://github.com/notifications/unsubscribe-auth/AFQjv698T9vM9V5DKPyOEg9T5VLpUC5Qks5rMRhCgaJpZM4LWSTj .

dustinfarris commented 7 years ago

This is what I had to do to make all of this work. The last piece of the puzzle was using ops in the mutation. I have to create a dummy "cached" query so that the createItem mutation knows to add subfields. That query has to match an actual query that will be used by the detail page. Both queries need the same op name and key. The mutation needs to specify the key in ops. (all this is necessary to prevent cashay from doing a second server request for data it already has)

@mattkrick am I on the right track here?

// controllers/new.js
...
// this just generates a new uuid to use
new_item_id: computed(function() { return uuid.v4() }).volatile()
...
<!-- new.hbs -->

{{new-item item_id=new_item_id create=(route-action "createItemAndRedirect")}}
// components/new-item.js

const newItemQuery = `
{
  item @cached(id: $item_id, type: "Item") {
    id
    name
  }
}
`;

const stateToComputed = (state, { item_id }) => {
  const { data: { item } } = cashay.query(newItemQuery, {
    op: 'item-detail',
    key: item_id,
    mutationHandlers: {
      createItem(optimistic, response, current) {
        if (optimistic) {
          Object.assign(current.item, optimistic.item_details);
          return current;
        }
      }
    },
    variables: { item_id }
  });
  return { item: Object.assign({}, item, { id: item_id }) };
};

const CreateItem = Component.extend({
  createItem: task(function *(changeset) {
    yield this.sendAction('create', Object.assign({}, this.get('item'), changeset.get('change')));
  }).drop(),

  layout: hbs`

  {{create-item-form
    changeset=(changeset item)
    submit=(perform createItem)
  }}

  `
});

export default connect(stateToComputed)(CreateItem);
// routes/application.js

...
actions: {
  createItemAndRedirect(item_details) {
    cashay.mutate('createItem', {
      variables: { item_details },
      ops: {
        'item-detail': item_details.id
      }
    });
    this.transitionTo('detail', item_details.id);
  }
}
// components/item-detail.js

// this is the same query used in new-item only without @cached
const itemQuery = `
{
  item (id: $item_id) {
    id
    name
  }
}
`;

const stateToComputed = (state, { item_id }) => {
  const { data: { item } } = cashay.query(itemQuery, {
    op: 'item-detail',
    key: item_id,
    variables: { item_id }
  });
  return { item };
};

...