davegravy
davegravy

Reputation: 934

How can I refetch() my Query only when new subscription data arrives?

Most examples use subscribeToMore within the query component but in my case the data returned by the query and subscription are very different so I think I need a discrete subscription component.

I have time series data being added to a database triggering onChangedWeatherIntervals subscription which returns summary info about the new time series info. If the new time series info matches the React client's filters it should request an updated histogram getHistogram query.

My test code:

<Query
  query={gql`
    {
      getHistogram (deviceID:"ARMS-NVM-P16", startTimestamp:1525201056) {
        columns {
          binWidth
          binHeight
          binCenter
        }
      }
     }
  `}
>
  {({
      loading: queryLoading,
      error: queryError,
      data: queryData,
      refetch
    }) => (
    <Subscription
      subscription={gql`
        subscription onChangedWeatherIntervals {
          onChangedWeatherIntervals {
            changedItems {
              deviceID
              timestamp
              siteKey
            }
          }
        }
      `}>
      {({
          data: subscriptionData,
          loading: subscriptionLoading,
          error: subscriptionError
        }) => {
        if (subscriptionData && !subscriptionLoading) {
          console.log("subscriptionData: ", subscriptionData);
          //TODO: Add code to inspect subscriptionData & determine if we refetch
          //refetch() //Uncommenting causes refetch loop after first server push
        }
        if (queryData && !queryLoading) {
          console.log("queryData: ", queryData.getHistogram.columns);
          return (<p>{queryData.getHistogram.columns[0].binWidth}</p>);
        }
        return null
      }}
    </Subscription>
  )
  }
</Query>

Because subscriptionLoading is true only before the first server push after mounting, I'm not sure the best way to distinguish re-renders from new subscriptionData. Should I store subscriptionData to state.subscriptionData and compare the two on each render?

Is there a more elegant approach to all of this?

Upvotes: 1

Views: 1380

Answers (1)

wei
wei

Reputation: 3482

Have you ever tried to use subscribeToMore on your Query? You can probably call refetch in its updateQuery prop.

UPDATE: I happened to face the same problem in my app and figured out a way to solve it. Feel free to check it out here. Basically you should avoid putting your subscription logic in a render method which will cause infinite refetch/re-rendering problem. The solution is to move the logic out of render method and put it into a lifecycle method like componentDidMount. That way subscribe won't be called again after a refetch is triggered.

Here's the code snippet for your case. (I used the hoc graphql from 'react-apollo' to help injecting query's refetch and subscribeToMore into props).

import { graphql } from 'react-apollo';

class YourComponent extends React.Component {
  componentDidMount() {
    const { data: { refetch, subscribeToMore }} = this.props;

    this.unsubscribe = subscribeToMore({
      document: <ON_CHANGED_WEATHER_INTERVALS>,
      updateQuery: (prev) => {
        refetch();
        return prev;
      },     
    });
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  render() {
    const { data: {queryData, loading, error } } = this.props;

    if (loading || error) return null;

    return <p>{queryData.getHistogram.columns[0].binWidth}</p>
  }
}

export default graphql(GET_HISTOGRAM, {
  options: {
    variables: {
      deviceID: 'ARMS-NVM-P16',
      startTimestamp:1525201056,
    },
  },
})(YourComponent)

Upvotes: 1

Related Questions