Apollo-client: Variable recalculation for each polling request

Created on 21 Feb 2018  路  18Comments  路  Source: apollographql/apollo-client

Intended outcome:
Use query polling option and recalculate variables for every request.

Actual outcome:
We have a time deppendend query variable, this variable is not propagated from the props, but it is calculated from current time in the query options function. But it's not recalculated when a new polling request is throwed. Is there a way how to enforce variables recalculation please?

const config = {
 options: (props) => {
     const { base } = props;
     // a dynamic variable calculated from current time, it is not recalculated
     const  from =  new Date().getTime() - base; 
     const variables = { from};
     const pollInterval = 5000;
     return { variables, pollInterval };
  }
};

How to reproduce the issue:
https://codesandbox.io/s/q3mjxyw7zw
I tried to simplify the issue and create a simple config with a current time dependend variable.
This variable is not recalculated.

Version

馃毀 in-triage

Most helpful comment

Can we revisit this issue? I have a dynamic variable that needs to be set at each pollingInterval.

All 18 comments

I'm also running into this issue. I calculate a variable based on certain parameters and am struggling to find a way to update the initial variables that polling uses if it changes.

Calling updateQuery doesn't seem to have the ability to update the variables as mentioned, either, which at first glance I thought it may be able to.

It looks like the function i use to calculate the property is being called and returning the correct variables (from inside options), but the polling query is still using the stale data from when the page was first loaded.

edit: relevant code to clarify

function getFoo () {
   ...
   return foo // returning correct value on new poll cycle
}


@graphql(query, {
  options: props => {
    return {
      variables: {
        foo: getFoo(),
        ...
      }
   },
   ...
})

But the network tab is using the original value of foo from the initial page load.

bump

Same problem with apollo client 2.2.7. At least i'm not the only one struggling with this problem

/label has-reproduction

Any update on this issue?

you could use the startPolling and stopPolling functions.
https://www.apollographql.com/docs/react/essentials/queries.html#refetching

Also hitting this issue

I also have this issue with the VUE implementation of Apollo I'm assuming its a limitation of the core Apollo client.

as @littletower mentioned if you call stopPolling before updating variables and then enable polling again - everything will work fine.

Thanks for reporting this. There hasn't been any activity here in quite some time, so we'll close this issue for now. If this is still a problem (using a modern version of Apollo Client), please let us know. Thanks!

Can we revisit this issue? I have a dynamic variable that needs to be set at each pollingInterval.

+1

I would like to see how it should be done as well, this is the workarround I implemented for us using hooks, maybe that helps getting inspired :) - https://gist.github.com/kjellski/42ae99e7b1d85a76d835274c255c6af0

// NOTE: not using poll here because the interval needs to change on every call.
// So we're using multiple hooks to achieve this:
// 1. `useInterval` to keep the recalculation with `setVariablesIntervalCallback` triggering
// 2. `useState` to have a clear state change when we called `setVariablesIntervalCallback`
// 3. `usePrevious` to have data while new data is beeing fetched so the table can stay intact
const Container = ({ children, someValue }) => {
  const [variables, setVariables] = useState({
    someValue,
    value: calculateValue(),
  })

  const setVariablesIntervalCallback = useCallback(() => {
    setVariables(variables => ({
      ...variables,
      value: calculateValue(),
    }))
  }, [setVariables])

  useInterval(setVariablesIntervalCallback, POLL_INTERVAL_IN_MS)

  const { data, loading, error } = useErrorHandlingQuery(
    queryHubViewDashboard,
    {
      skip: !hubId,
      variables,
    },
  )

  const previousData = usePrevious(data)

  return children({
    // we only want the last fetched data if there's no new one, this is helping while refetching to still fill the UI
    data: data || previousData, 
    loading,
    error,
  })
}

Feels like onCompleted should fire after each polling request is completed but that doesn't seem to be the case? I'm storing the current date in a useRef hook which is then passed as a variable to my useQuery hook. However updating the ref in onCompleted seems to only occur once (I'm guessing after the first successful query).

Edit: I ended up also doing a hacky fix using useInterval hook in combination with the useLazyQuery hook. Set up the lazy query, then in useInterval I call it with the latest date as a variable.

const [fetchQuery, { loading, data }] = useLazyQuery<
    MyQuery,
    MyQueryVariables
  >(myQuery, {
    fetchPolicy: "network-only",
    query: myQuery,
  });

  useInterval(() => {
    if (!shouldISkip) {
      return;
    }

    const dateVar = new Date();
    getStreamHealth({
      variables: {
        userID : userID || "",
        dateVar : dateVar .toISOString(),
      },
    });
  }, FETCH_INTERVAL);

Note I'm doing all this in a custom hook that feeds a context with the latest polled result.

In order ro work around the issue I am using stopPolling then refetch on every parameter change, and then onCompeletd with startPolling

const handleParamChange = (value: string) => {
    setParam(value);
    stopPolling();
    refetch();
  };

   const {
    data,
    refetch,
    stopPolling,
    startPolling,
  } = useQuery(QUERY, {
    onCompleted: () => {
      startPolling(POLL_INTERVAL);
    },
    variables: {
      param: param,
  });

@yuval-hazaz how are you updating the variables? Is there something magic happening in setParam?

Setting notifyOnNetworkStatusChange: true fixed this issue for me:

const date = new Date().toISOString()

const result = useQuery(QUERY, {
  notifyOnNetworkStatusChange: true,
  variables: {
    date,
  },
  pollInterval: POLL_INTERVAL,
})

@crhallen I found that fixed the issue and let me use my incremented pollInterval using an onCompleted function but now the component is flashing every single time it fires.

Was this page helpful?
0 / 5 - 0 ratings