Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

getFragment from a dynamic component in Relay

My use case is that I have a Node application that consumes data from a CMS, and in that CMS I give the users the ability to select a React Component as a "Layout". I'd like for Relay to be able to get a GraphQL Fragment from that dynamically select component. When the Layout's parent component mounts, it goes through its query and gets the layout component it needs and sets a Relay variable - it then needs to get a fragment from that component. Is there a way to do that?

Here is the parent level query:

export default Relay.createContainer(WordpressPage, {

initialVariables:{
    Component: null,
    page: null,
    showPosts: false,
    limit: 5
},

prepareVariables(prevVars){
    return{
        ...prevVars,
        showPosts: true
    }
},

fragments: {
  viewer: ({Component, showPosts, limit}) => Relay.QL`
    fragment on User {
      ${PostList.getFragment("viewer", {limit:limit}).if(showPosts)},
        page(post_name:$page){
          id,
          post_title,
          post_type,
          post_content,
          thumbnail,
          layout{
           meta_value
          }
        }
      }
    `,
  },
});

As you can see, it queries and gets a layout field. When it mounts, it sets the Relay Component variable to be a React Component. Instead of "PostList.getFragment", I'd really like to be able to do a Component.getFragment.

like image 763
Ramsay Lanier Avatar asked Jan 28 '16 17:01

Ramsay Lanier


1 Answers

What you must do here is to interpolate all possible fragment references into the query. Starting with Relay v0.7.1 you will be able to interpolate into your query an array of fragment references:

const COMPONENTS = [
  [PostList, 'showPosts'],
  [OtherKindOfList, 'showOthers'],
  /* ... */
];

static initialVariables = {
  showPosts: false,
  showOthers: false,
  /* ... */
};

fragments: {
  viewer: variables => Relay.QL`
    fragment on User {
      ${COMPONENTS.map(([Component, variableName]) => {
        const condition = variables[variableName];
        return Component
          .getFragment('viewer', {limit: variables.limit})
          .if(condition);
      })},
      # ...
    `,
  },
});

Warning: Presently there is a limitation in Relay that requires that the interpolated expression return either:

  • 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 original poster's use case was for a route-conditional function that returns an array of fragment references, which is not yet supported. See the comments for more information. See also facebook/relay/issues/896

like image 155
steveluscher Avatar answered Oct 19 '22 23:10

steveluscher