Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support interpolation of route-conditional fragments that return an array of fragment references #896

Closed
steveluscher opened this issue Mar 3, 2016 · 11 comments

Comments

@steveluscher
Copy link
Contributor

Presently there is a limitation in Relay that requires that any interpolation you perform in a Relay.QL block must evaluate to:

  • a fragment reference

    ${Foo.getFragment('viewer')}

  • an array of fragment references

    ${COMPONENTS.map(c => c.getFragment('viewer'))}

  • a route-conditional function that returns exactly one fragment reference

    ${route => COMPONENTS[route].getFragment('viewer')}

The task at hand is to enable the interpolation of route-conditional functions that return an array of fragment references.

${route => COMPONENTS.map(c => 
  c.getFragment('treasure').if(variables[`${route}Unlocked`])
)}
@steveluscher
Copy link
Contributor Author

Origin issue: http://stackoverflow.com/a/35471007/802047

@NevilleS
Copy link
Contributor

NevilleS commented Mar 4, 2016

This is the same issue too: #775

@shaimo
Copy link

shaimo commented Mar 26, 2016

I have a similar use case. Can someone please elaborate on that 'route-conditional function'? Where is that 'route' parameter coming from? Thanks

@steveluscher
Copy link
Contributor Author

If you interpolate a function into your query:

viewer: () => RelayQL`
  fragment on Viewer {
    ${route => ...}
  }

That function will receive the routeName of the Relay.Route you used with Relay.RootContainer as its first argument, and will expect that you return a single fragment reference (eg. that which is returned from Container.getFragment('viewer')).

@shaimo
Copy link

shaimo commented Mar 27, 2016

@steveluscher Thanks. I guess it won't work for me. I'm using react-router so the given route seems like something that was generated by it. I actually need to choose the component based on a parameter (called 'hash' in my case), but I don't see any way to do it...

ghost pushed a commit that referenced this issue Jun 25, 2016
Summary:
Gave #896 a try.

modifies `createNode` and `getChildren` to possibly return an array of nodes when a route conditional fragment reference returns an array.

Let me know what you think, It's seems weird to have `createNode` possibly return an Array but I'm not sure how it could be done without it!

[Updates to original PR from wincent: switched `var` to `let`/`const`, avoided an array allocation in `getChildren`.]

Closes #915

Reviewed By: josephsavona

Differential Revision: D3484897

Pulled By: wincent

fbshipit-source-id: 0d101bf63e1a7e5669fc4bd9094e0d35c17c8c40
@steveluscher
Copy link
Contributor Author

Closed by #915.

@taion
Copy link
Contributor

taion commented Jul 25, 2016

Apologies if I'm missing something here, but with the

${route => COMPONENTS.map(c => 
  c.getFragment('treasure').if(variables[`${route}Unlocked`])
)}

syntax – is it the case that you must define this function inline to the fragment if you want to have access to variables? In other words, I couldn't have a separate:

const conditionalFragment = route => (
  COMPONENTS[route.name].getFragment('treasure', variables)
);

@josephsavona
Copy link
Contributor

@taion You can define the function wherever you want, as long as you make sure that variables are in scope.

@taion
Copy link
Contributor

taion commented Jul 25, 2016

The problem is that variables are only in scope if I am defining this within the fragment. If I want a reusable conditional fragment as above, where I define it outside the fragment definition in Relay.createContainer, there's no way for variables to be in scope.

@josephsavona
Copy link
Contributor

@taion This is just like any function - if you need access to variables in the helper, you have to pass it:

const conditionalFragment = (route, variables) => (
  COMPONENTS[route.name].getFragment('treasure', variables)
);
createContainer(Foo, {
  fragments: {
    foo: variables => Relay.QL`fragment on Foo {
      ${route => conditionalFragment(route, variables)}
    `,
  },
});

@taion
Copy link
Contributor

taion commented Jul 25, 2016

Oops, massive brain fart on my part. 😑

Would it make sense for the signature for conditional fragments to in fact be:

(RelayMetaRoute, Variables) => ?RelayQueryNode | Array<?RelayQueryNode>

It'd be a minor quality-of-life improvement in the case above, since it'd allow avoiding the apparently unnecessary extra function bind above.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants