javascriptreactjsgraphqlrelayjsreact-router-relay

Is react-router-relay inconsistent with the Relay pattern?


I'm using react-router-relay in a project. The design seems off to me given that every component basically ends up with a fragment having the same name as the root query. Shouldn't each component be able to have uniquely named fragments of any arbitrary type under the root query? Is this possible using this package or is my thinking flawed here?

Edit: Perhaps my question was a bit vague. My problem is that there are essentially two rules for the queries attribute defined by react-router-relay that enforce what seems to me to be a weird design pattern. Those two rules are:

  1. Each query can only go "one level" deep.
  2. Each query must map to a fragment with an identical name on the component that uses it.

This leaves you with a scenario whereby you either:

  1. Use the same "viewer" query for every component and define a complimentary "viewer" fragment on each component. These fragments would all define different data requirements, despite having the same name, which seems very confusing.
  2. You create unique fragment names for different components and then repeat the same exact root query with different names depending on the type of data you want to fetch, which seems downright silly.

Solution

  • Good question. When you're dealing with Relay, you're thinking is correct in that every component should have its own fragment so that the query itself maps exactly to the data needed for that particular component. The naming of the fragments can be however you like them named, but the type cannot be arbitrary. It must be a declared type underneath the Root Query object (or whatever field you are appending the fragment to). Otherwise the fragment will throw an error saying that you cannot query that type on Query or field.

    For example:

    var componentOneFragment = Relay.QL`
        fragment on User {
            name
        }
    `;
    

    One thing to note here is that you don't need to have a name for fragments like fragment userFragment on User { ... }. This will give you more flexibility when referencing component fragments dynamically from Relay queries in your router by declaring ${Component.getFragment(componentOneFragment)}. Hope this helps!

    EDIT:

    Use the same "viewer" query for every component and define a complimentary "viewer" fragment on each component. These fragments would all define different data requirements, despite having the same name, which seems very confusing.

    Although the fact that the identical names of the fragments may seem confusing, this is the best way to think about things. Each component does indeed have different data requirements, so naturally their Relay containers will have different fragments, but still under the same fragment name.

    This fragment may be included in one of your Relay containers that need User data:

    const WidgetList = Relay.createContainer(/* ... */, {
      initialVariables: {
        color: null,
        size: null,
        limit: null
      },
    
      fragments: {
        viewer: () => Relay.QL`
          fragment on User {
            widgets(color: $color, size: $size, first: $limit) {
              edges {
                node {
                  name,
                },
              },
            },
          }
        `
      }
    });
    

    While this fragment (with still the same name) may be included in another Relay container that needs Widget data:

    const ActionsList = Relay.createContainer(/* ... */, {
      initialVariables: {
        input: null
      },
    
      fragments: {
        viewer: () => Relay.QL`
          fragment on Widget {
            actions(input: $input) {
              edges {
                node {
                  name,
                },
              },
            },
          }
        `
      }
    });
    

    These can both be used dynamically (i.e. $Component.getFragment('viewer')) in the same GraphQL query as long as User and Widget are both types under the Root Query object.