reactjsrelayjsrelayreact-router-relayreact-relay

Retrieving variables for a Relay query fragment from an external store


When writing a GraphQL query fragment as part of a RelayContainer, I've been struggling with how to set query variables in certain situations. The basics outlined in the documentation are simple enough. But given the static nature of these queries and how a RelayContainer is a higher-order React Component, I'm not sure the best way to handle this when the input needed for a query is an object from an external source such as a Redux store. I don't have any way to retrieve data from that store before the component loads, so as a workaround I've been using the @include directive and setting a ready variable to true after the component loads:

class ListData extends React.Component {

  // ... proptypes 

  componentWillMount() {
    const { listDataInfo } = this.context.store.getState(); // Retrieve from Redux store

    this.props.relay.setVariables({
      input: {
        id: listDataInfo.id,
        user: listDataInfo.user
      },
      inputReady: true,
    });

    // The query will now execute and fetch all the data
  }

  render() {
    return (
      {/* view */}
    )
  }
}

export default Relay.createContainer(ListData, {
  initialVariables: {
    input: null,
    inputReady: false,
  },
  fragments: {
    listData: () => Relay.QL`
      fragment on ListDataQuery {
        listData(input: $input) @include(if: $inputReady) {
          city
          state
          zip
          phone
        }
      }
    `,
  },
});

In the end this works as expected, but I'm concerned that this is more of a hack, and that I'm missing something important with how Relay should be handling query variables. Is there a better way to do this? The prepareVariables looked like it could be useful but I still wouldn't have access to my data store. Here are my apparent options:

  1. Keep it as is
  2. Convert it to a mutation that will execute in a component lifecycle method. I'd rather not do this as it removes the data binding to the component.
  3. Use a custom RelayNetworkLayer (which has access to my store) to intercept my queries and set the variables from an external store there. This also feels like a hack.

Solution

  • I found this gist that accomplishes exactly what I'm looking for. It lets you pass in redux state variables via @container decorator with the property variablesFromState. Essentially it will return either a standard Relay Container (if variablesFromState is not set) or a Relay Container wrapped in a redux Provider wrapper with the redux store instance passed in.