mattkrick / cashay

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

remove id from the @cached directive #127

Open mattkrick opened 7 years ago

mattkrick commented 7 years ago

the id, ids fields are pretty useless & can use a pre-cached value causing source.id to come up undefined. It's not terrible, but it causes an extra render. Plus this makes for a smaller, more opinionated API.

jordanh commented 7 years ago

I saw these changes reflected in the latest Action PR. Could you help illustrate how this works without id? I must admit, I've been a little foggy on the role of resolveCached.

Let's use an example:

const projectCardSubQuery = `
query {
  project @cached(type: "Project") {
    content
    id
    status
    teamMemberId
    updatedAt
    teamMember @cached(type: "TeamMember") {
      id
      picture
      preferredName
    }
    team @cached(type: "Team") {
      id
      name
    }
  }
}
`;

const mapStateToProps = (state, props) => {
  const userId = state.auth.obj.sub;
  const [teamId] = props.project.id.split('::');
  const projectId = props.project.id;
  const {project} = cashay.query(projectCardSubQuery, {
    op: 'projectCardContainer',
    key: projectId,
    variables: {projectId},
    resolveCached: {
      project: () => projectId,
      team: (source) => (doc) => source.id.startsWith(doc.id),
      // example of returning a string instead of a function so it runs in O(1)
      teamMember: (source) => source.teamMemberId
    },
  }).data;

So resolveCached for project returns a single parametric projectId; teamMember returns the id from the source document; and team returns a function that returns a bool.

How does this work? does the resolveCached project function work by indexing into the normalized collection of Projects? Then the source parameter passed to the resolvedCached teamMember function is a Project? And then Team is found (O(n)) by ensuring that the Project id starts with the Team Id?

Is that right?

mattkrick commented 7 years ago

Yep! I could probably elucidate this a little better in the docs, but basically each method in resolveCached is a function that can return a string, an array of strings, or a function.

If it returns a string, in this case projectId, it with return store.getState().cashay.entities[type='Project'][projectId].

Next, look at the teamMember method. It also returns a string source.teamMemberId. The source is the parent object (just like in a GraphQL resolve function). So in this case:

const id = `store.getState().cashay.entities[type='Project'][projectId].teamMemberId`
return store.getState().cashay.entities[type='TeamMember'][id]

Finally, just like you said, the team method returns a function, which will run in O(n) over every single Team entity. It returns a single value. For an array of values, the type would be type: [Team].

jordanh commented 7 years ago

They way you just wrote about it here is super, super clear. This would be great for the docs!